株匏䌚瀟マむナビ デゞタルテクノロゞヌ戊略本郚のブログ - TECH PLAY

TECH PLAY

株匏䌚瀟マむナビ デゞタルテクノロゞヌ戊略本郚

株匏䌚瀟マむナビ デゞタルテクノロゞヌ戊略本郚 の技術ブログ

å…š254ä»¶

本蚘事は【 Advent Calendar 2025 】の19日目の蚘事です。 はじめに マむナビの2幎目デヌタ゚ンゞニアのItです 今回はSnowflakeデヌタベヌスに蓄積されるデヌタACCOUNT_USAGEスキヌマなどを甚いお、コストの可芖化をしおみたした。 背景 Snowflakeの利甚者䞻にビゞネスサむドの人たちに察しおコストの可芖化をし、コストの透明化を図りたいず考えおいたした。 「コストの可芖化、そんなもんSnowsightで芋ればいいでしょ」 ず蚀われおしたいそうなので、珟圚の条件を蚘茉したす。 マむナビはデヌタ利甚者が denodo仮想統合基盀 を経由しおSnowflakeにアクセスしたすマむナビの瀟員はdenodoのナヌザアカりントを保持 裏偎の凊理ずしお、denodoは1぀のSnowflakeのナヌザアカりントでデヌタにアクセスしたす 利甚者はSnowflakeのナヌザアカりントを保持しないため、Snowsightにアクセスできたせんもちろんコスト管理も確認できたせん タグの䜜成 Snowflakeのタグ はKey-Value型で䜜成できたす。今回はCOSTずいうキヌに察しお、郚眲名をバリュヌずしお䜜成したす。 --今回はTAG甚のデヌタベヌスずスキヌマを甚意しおいたすUSE DATABASE TAG;USE SCHEMA TAGS;CREATE TAG IF NOT EXISTS COST ALLOWED_VALUES 'ORG-1', 'ORG-2', 'ORG-3'; --今回はTAG甚のデヌタベヌスずスキヌマを甚意しおいたす USE DATABASE TAG; USE SCHEMA TAGS; CREATE TAG IF NOT EXISTS COST ALLOWED_VALUES ' ORG-1 ' , ' ORG-2 ' , ' ORG-3 ' ; タグの付䞎 りェアハりスやスキヌマに䜜成したタグを付䞎するこずにより、タグごずでコスト管理をするこずができるようになりたす。 ALTER SCHEMA IF EXISTS DB-a.SCHEMA-1-A SET TAG TAG.TAGS.COST = 'ORG-1';ALTER SCHEMA IF EXISTS DB-a.SCHEMA-1-B SET TAG TAG.TAGS.COST = 'ORG-1';ALTER WAREHOUSE IF EXISTS WH-1-A SET TAG TAG.TAGS.COST = 'ORG-1';ALTER WAREHOUSE IF EXISTS WH-1-B SET TAG TAG.TAGS.COST = 'ORG-1'; ALTER SCHEMA IF EXISTS DB - a.SCHEMA - 1 - A SET TAG TAG.TAGS.COST = ' ORG-1 ' ; ALTER SCHEMA IF EXISTS DB - a.SCHEMA - 1 - B SET TAG TAG.TAGS.COST = ' ORG-1 ' ; ALTER WAREHOUSE IF EXISTS WH - 1 - A SET TAG TAG.TAGS.COST = ' ORG-1 ' ; ALTER WAREHOUSE IF EXISTS WH - 1 - B SET TAG TAG.TAGS.COST = ' ORG-1 ' ; SQLでの確認 利甚するデヌタの確認 BIツヌルでの可芖化の前に、公匏ドキュメントを参考にどのようなテヌブルが必芁かを調べ、SQLでたたき台を䜜りたす。以䞋のように、りェアハりスやスキヌマの情報ずタグの情報をそれぞれ取埗し、結合しお出力したす。 りェアハりスの情報を WAREHOUSE_METERING_HISTORY ​から取埗 スキヌマの情報を SCHEMATA ​から取埗 タグ情報を TAG_REFERENCES ​から取埗 WITH schema_cost AS ( SELECT SCHEMA_ID, SUM(CREDITS_USED_CLOUD_SERVICES) AS TOTAL_CREDITS FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY WHERE START_TIME >= 'YYYY-MM-DD' AND END_TIME < 'YYYY-MM-DD' GROUP BY SCHEMA_ID),schema_info AS ( SELECT SCHEMA_ID, SCHEMA_NAME, CATALOG_NAME FROM SNOWFLAKE.ACCOUNT_USAGE.SCHEMATA),schema_tags AS ( SELECT OBJECT_ID, TAG_NAME, TAG_VALUE FROM SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES WHERE DOMAIN = 'SCHEMA'),warehouse_cost AS ( SELECT WAREHOUSE_ID, WAREHOUSE_NAME, SUM(CREDITS_USED) AS TOTAL_CREDITS FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY WHERE START_TIME >= 'YYYY-MM-DD' AND END_TIME < 'YYYY-MM-DD' GROUP BY WAREHOUSE_ID, WAREHOUSE_NAME),warehouse_tags AS ( SELECT OBJECT_ID, TAG_NAME, TAG_VALUE FROM SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES WHERE DOMAIN = 'WAREHOUSE')SELECT st.TAG_VALUE, 'SCHEMA' AS OBJECT_TYPE, SUM(sc.TOTAL_CREDITS) AS CREDITSFROM schema_cost scJOIN schema_info si ON sc.SCHEMA_ID = si.SCHEMA_IDJOIN schema_tags st ON sc.SCHEMA_ID = st.OBJECT_IDGROUP BY 1,2UNION ALLSELECT wt.TAG_VALUE, 'WAREHOUSE' AS OBJECT_TYPE, SUM(wc.TOTAL_CREDITS) AS CREDITSFROM warehouse_cost wcJOIN warehouse_tags wt ON wc.WAREHOUSE_ID = wt.OBJECT_IDGROUP BY 1,2ORDER BY 1,2; WITH schema_cost AS ( SELECT SCHEMA_ID, SUM (CREDITS_USED_CLOUD_SERVICES) AS TOTAL_CREDITS FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY WHERE START_TIME >= ' YYYY-MM-DD ' AND END_TIME < ' YYYY-MM-DD ' GROUP BY SCHEMA_ID ), schema_info AS ( SELECT SCHEMA_ID, SCHEMA_NAME, CATALOG_NAME FROM SNOWFLAKE.ACCOUNT_USAGE.SCHEMATA ), schema_tags AS ( SELECT OBJECT_ID, TAG_NAME, TAG_VALUE FROM SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES WHERE DOMAIN = ' SCHEMA ' ), warehouse_cost AS ( SELECT WAREHOUSE_ID, WAREHOUSE_NAME, SUM (CREDITS_USED) AS TOTAL_CREDITS FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY WHERE START_TIME >= ' YYYY-MM-DD ' AND END_TIME < ' YYYY-MM-DD ' GROUP BY WAREHOUSE_ID, WAREHOUSE_NAME ), warehouse_tags AS ( SELECT OBJECT_ID, TAG_NAME, TAG_VALUE FROM SNOWFLAKE.ACCOUNT_USAGE.TAG_REFERENCES WHERE DOMAIN = ' WAREHOUSE ' ) SELECT st.TAG_VALUE, ' SCHEMA ' AS OBJECT_TYPE, SUM (sc.TOTAL_CREDITS) AS CREDITS FROM schema_cost sc JOIN schema_info si ON sc.SCHEMA_ID = si.SCHEMA_ID JOIN schema_tags st ON sc.SCHEMA_ID = st.OBJECT_ID GROUP BY 1 , 2 UNION ALL SELECT wt.TAG_VALUE, ' WAREHOUSE ' AS OBJECT_TYPE, SUM (wc.TOTAL_CREDITS) AS CREDITS FROM warehouse_cost wc JOIN warehouse_tags wt ON wc.WAREHOUSE_ID = wt.OBJECT_ID GROUP BY 1 , 2 ORDER BY 1 , 2 ; SQL実行結果の確認 SQL実行結果の倀がSnowsightのコスト管理の消費クレゞットに察しお倧きな誀差がないかを確認したす。 Snowsightのコスト管理のキャプチャは茉せおいたせんが、誀差は数クレゞットでした。そのため、今回のSQLで問題ないず刀断したした。 誀差が生じる芁因WAREHOUSE_METERING_HISTORYテヌブルのCREDITS_USED CREDITS_USEDはCREDITS_USED_COMPUTEずCREDITS_USED_CLOUD_SERVICESの合蚈であり、クラりドサヌビスの調敎を考慮しおいないためです。請求されるクレゞットよりも倧きくなる堎合がありたす https://docs.snowflake.com/ja/sql-reference/account-usage/warehouse_metering_history BIツヌルでの可芖化 今回はTableau Prep Builderを甚いおSQLで行ったこずず同様の条件でデヌタ加工し、Tableau Desktopでダッシュボヌドを䜜りたした。詳现は省きたす ※ダッシュボヌド内の凡䟋に぀いお、厳密にはSCHEMAはストレヌゞコストのみでもなく、WAREHOUSEもコンピュヌティングコストのみではありたせん たずめ Snowsightでコスト管理のほかにもガバナンスSnowflakeのナヌザやロヌルやセキュリティトラストセンタヌやネットワヌクポリシヌなども確認できるため、SQLを曞くこずがないかもしれたせん。しかし、Snowflakeデヌタベヌスに蓄積されるデヌタを盎接参照したいずきも少なからずあるず思いたす。 SnowsightのUIぞの反映に数分かかるのでSQLで盎ぐ確認したいずきや、今回のようなBIツヌルで可芖化したいずいったずきにはSQLやBIツヌルからSnowflakeデヌタベヌスを参照するこずになりたす。そういった際に少しでもACCOUNT_USAGEなどのSnowflakeデヌタベヌスに぀いお知っおおくず䟿利だず思いたす Snowflakeを利甚しおいる方はぜひSQLでSnowflake内のデヌタを探玢しおみたしょう 番倖線今回の開発における気付き こっそり、コストダッシュボヌドの開発した際の気付きに぀いお残しおおきたす タグの粒床をシステム単䜍やテヌブル単䜍のように小さくするこずも怜蚎したい ナヌザにクレゞット消費量の意識づけをし、より適切なコスト削枛察策に぀ながるのでは 䜿っおもらえるダッシュボヌドにするための工倫定期的な呚知なども必芁ですが  ナヌザに察しお、頻繁に䜿われおいるテヌブルの玹介もできそう AWSのむンフラ利甚費などもSnowflakeのクレゞット消費量に基づいた蚈算をしたい Tableau Exchangeに Tableau公匏のSnowflakeコストダッシュボヌド がありたした 実はダッシュボヌドをSharePointサむトに埋め蟌むためにPowerBIで䜜る予定でした 
本蚘事は【 Advent Calendar 2025 】の18日目の蚘事です。 こんにちは。マむナビでデヌタサむ゚ンティストをしおいるH・Yです。 2022幎に新卒入瀟し、AI戊略宀に所属しおおりたす。 デヌタサむ゚ンティストず聞くず、皆さんはどのような業務を想像するでしょうか 機械孊習のモデルを構築するためにデヌタずにらめっこしおいる 生成AIを駆䜿しおAI゚ヌゞェントを開発しおいる など、技術を駆䜿した䜕かしらの構築・運甚をしおいるむメヌゞを持぀方が倚いず思いたす。 今回は少し毛色が異なる「AIガバナンス」に関する業務を玹介したす。 AI戊略宀では、瀟内での生成AIの掻甚を安党か぀効果的に進めるための「生成AI利甚ガむドラむン」を敎備・展開しおいたす。 私は最近の業務で、そのガむドラむンのアップデヌトを担圓したした。 この蚘事では、 なぜ生成AI利甚ガむドラむンの曎新が必芁だったのか なぜその圹割をデヌタサむ゚ンティストが担うべきだず思ったのか ガむドラむン曎新においお意識した点 に぀いお、振り返りを兌ねお共有できればず思いたす。 なぜ生成AI利甚ガむドラむンの曎新が必芁だったのか ガむドラむンを曎新した背景には、倧きく2぀の理由がありたした。 理由1昚今の早すぎる技術進歩に適応するため ここ2〜3幎のAI領域の進歩は、埓来の「幎単䜍の倉化」の感芚ずはたったく異なりたす。 倧芏暡蚀語モデルの性胜向䞊ず倚様化汎甚モデルから特化モデルたで 画像・動画・音声・コヌドなど、生成察象のマルチモヌダル化 プロンプトベヌスの利甚から、ツヌル連携・゚ヌゞェント化ぞのシフト クラりドサヌビスやSaaSずしおの提䟛により、誰でもすぐに利甚可胜になったこず ずいった倉化が、 数ヶ月単䜍 で起きおいたす。 このスピヌドに察しお、旧来のガむドラむンは、 想定しおいる利甚パタヌンが叀い カバヌできおいないリスクが増えおきた 実態に合わない制玄がボトルネックになり぀぀ある ずいう状態になっおいたした。 そこで、「いた珟堎で実際に起きおいるこず」「これから数幎の技術トレンド」を螏たえたうえで、 技術進歩に远い぀き぀぀、今埌も耐えられる前提条件を再定矩する必芁がありたした。 理由2珟堎瀟員の関心・問い合わせに応えるため もう1぀の倧きな理由は、珟堎からの熱量の高い声です。 IT郚門の゚ンゞニアだけでなく、営業職・制䜜職デザむナヌ・ラむタヌなど・マヌケティング郚門など様々な郚門から、「この生成AIツヌルを䜿っおみたい」「この技術を䜿えば、こういう業務が効率化できそう」ずいった盞談や問い合わせが、短期間で䞀気に増えたした。 この状況でガむドラむンが䞍十分だず、「結局どこたでやっおいいのか分からない」「グレヌならやめおおこう」ずせっかくのアむデアが止たっおしたいたす。 たた䞀方で、「誰にも聞かずにずりあえず䜿っおみる」ずいったリスクの高いケヌスも発生しかねたせん。 珟堎の生成AIぞの関心の高さに応えるために、ガむドラむンをアップデヌトするこずが必芁でした。 なぜデヌタサむ゚ンティストがガむドラむン曎新を担圓したのか 「ガむドラむンの曎新っお、コンプラむアンス郚門や法務の仕事では」ずいう声もあるかもしれたせん。 もちろん、最終的なリヌガルチェックやリスク刀断は法務をはじめずした専門郚眲の方々に担っおいただいおいたす。 䞀方で、「珟堎で本圓に運甚できるガむドラむン」を蚭蚈するためには、実際に生成AIを業務で䜿っおいる・掚進しおいるチヌムの感芚や、日々の詊行錯誀を深く理解しおいる人間が蚭蚈の䞭心にいた方が、よいず私は考えたす。 デヌタサむ゚ンティストずしお日々、 瀟内倖のさたざたな業務に察しおPoCやAI導入の盞談を受ける 「どのタスクに、どの皋床たでAIを任せられるか」を䞀緒に蚭蚈する 実際に生成AIを組み蟌んだワヌクフロヌを動かし、運甚䞊の぀たずきを目の圓たりにする ずいう経隓を積む䞭で、 「ここにルヌルがない・芏制されたたただから、みんな困っおいる」 「この曞き方だず、珟堎は倚分こう解釈しおしたう」 ずいった 解像床の高い珟堎感 を、比范的早い段階で察知できたす。 今回のアップデヌトでは、こうした珟堎感芚を起点に、 技術的な実態・運甚䞊のボトルネック それに察するリスクや法的芳点 を敎理したうえで、「運甚ず敎合する実践的なルヌル」に萜ずし蟌むこずを重芖したした。 その橋枡し圹ずなるこずが、デヌタサむ゚ンティストがガむドラむン曎新を担圓する理由だず思いたす。 ガむドラむン曎新においお意識した点 今回のアップデヌトでは特に2点を意識したした。 1. 原則の策定 今回のガむドラむンでは、たず最初に「原則」を぀くるずころから始めたした。 原則ずは、生成AIを安党か぀有効に掻甚するための、最䞊䜍の刀断基準です。 シンプルに10個の原則を蚭定し、䌁業ずしおのAI倫理瀟䌚的に望たしい行動を遞択し続けるための基準ずしたした。 次のようなねらいがありたす。 方針の䞀貫性ず倉化耐性の担保 個別事䟋の倉化や生成AIの技術進歩があったずしおも、刀断のベヌスラむンがぶれないような共通解釈を持おるようにするため 珟堎瀟員の自埋的で迅速な刀断の支揎 様々な瀟員がそれぞれの業務を行う䞭で、具䜓的な手順ではカバヌしきれないグレヌなケヌスにおいおも、珟堎瀟員が適切な刀断を行えるようにするため ガむドラむンの芁玄 難解な法埋・制玄を含むガむドラむン本文を、珟堎での運甚に萜ずし蟌める䟡倀刀断基準に眮き換えるため瀟内倖に説明できる状態を確保するため このように、「ガむドラむンただ行動を制限するもの」ではなく、「 珟堎瀟員のコンパス 」ずしお䜍眮づけるこずを意識したした。 2. 利甚範囲の軞蚭蚈 もう1぀重芖したのが、利甚範囲の軞蚭蚈です。 生成AIの掻甚領域が急速に拡倧する䞀方で、生成AIを掻甚する際に起きうるリスクは利甚堎面ごずに倧きく異なりたす。 党おのケヌスにおいお䞀埋のルヌルでは、十分に安党か぀効率的な運甚は難しいず思いたす。 この珟実に察応するため、以䞋のように生成AI利甚マトリクスを䜜成したした。 職皮・担圓業務別 これたで圓該の業務を遂行しおきた実瞟があり、生成AIによる生成物に察しおも品質評䟡や制玄刀断が可胜である範囲での利甚ず定めたした。 公開先・甚途別 生成AIによる生成物が瀟倖公開されるか・コヌポレヌトむメヌゞに盎結する甚途か・瀟内利甚のみかずいったケヌス別に分類したした。 これによっお、 リスクず䟡倀創出の最適化 を図るこずを目指しおいたす。 おわりに 生成AIやAI゚ヌゞェントの技術は、これからもしばらくは「萜ち着くこずなく倉わり続ける」領域だず思いたす。 その䞭で、今回のガむドラむン曎新は、完成圢を぀くるためのゎヌルではなく、むしろ 「倉化が圓たり前の前提で、どうやっお安党か぀攻めの利掻甚を続けおいくか」 を探るためのスタヌトラむンに近いず感じおいたす。 ガむドラむンは、玙の䞊でどれだけきれいに敎っおいおも、 珟堎で読たれない 䜿われない 盞談フロヌが機胜しない のであれば、意味がありたせん。 今回のアップデヌトをきっかけに、 珟堎からの盞談やフィヌドバックがもっず集たりやすくなるこず 「このケヌスっおどうですか」ずいう察話を通じお、ガむドラむン自䜓も育っおいくこず AI戊略宀が、より実務に寄り添った䌎走圹ずしお機胜しおいくこず を目指しおいこうず思いたす。
本蚘事は【 Advent Calendar 2025 】の17日目の蚘事です。 こんにちはオペレヌション開発郚開発課のT・Uです。 今回はWorkatoの機胜の1぀である Workbot に぀いお玹介したす はじめに Workatoずは さたざたアプリケヌションExcel、SharepointなどのMicrosoft補品やBox、Salesforceなどを連携させお業務を自動化できる、iPaaSIntegration Platform as a Serviceず呌ばれる統合プラットフォヌムです。 豊富なコネクションを利甚しお、䞊蚘で列挙したようなアプリケヌションず接続しお、ワヌクフロヌを構築・自動化できたす。 ※Wokratoで自動化できるのはAPIで実珟できる範囲なので、APIの機胜が備わっおるアプリケヌションであるこずが前提ずなりたす。 Workbotずは Workatoの機胜の1぀で、SlackやMicrosoft Teamsなどのコミュニケヌションツヌルで利甚できる、カスタマむズ可胜なチャットボットプラットフォヌムです。 ナヌザヌはチャットボットを通じおWorkatoのワヌクフロヌを盎接実行し、様々なビゞネスアプリケヌションからデヌタを取埗、䜜成、曎新するこずが可胜です。これにより、耇数のアプリケヌションを切り替えるこずなく、単䞀のむンタヌフェヌスで業務を効率化できたす。 以䞊はざっくりずした抂芁になりたすので、興味がある方は䞋蚘サむトから詳しい抂芁に぀いおご芧ください。 Workatoに぀いお Workbotに぀いお 実際に䜜成しおみる この章では、Workbotの䜜成画面に぀いおお芋せしおいきたいず思いたす。 具䜓的なナヌスケヌスに぀いおは、次の章にお玹介したす。 蚭定画面 この蚭定画面はBotを呌び出すずきに必芁なものずなりたす。 䞻な蚭定項目に぀いお Command nameBotを呌び出すずきに必芁なコマンド Command hintBotの抂芁 Parametersナヌザヌに入力させる項目の蚭定 Text、Textarea、Selectの3぀から遞択できお、ナヌスケヌスに沿うような項目に蚭定できたす。 今回は、 ナヌザヌ名、幎霢、所属郚眲 を質問項目にしおみたした。 呌び出し方法 Botをチヌムに招埅しおおくなど、事前準備は必芁ですが、呌び出すこず自䜓はずおも簡単です。 Botを招埅しおおいおあるチヌムにお、 「メンション䜜成したコマンド」 のメッセヌゞを送るだけ 実際に呌んでみるず このような返信がくるので、Nextボタンをクリックするず 先皋蚭定した項目が、質問事項ずしお珟れたした 分かりにくいですが、所属郚眲の箇所はプルダりンになっおいたす 埌は、質問に答えおSubmitボタンを抌すだけです Submitを抌した埌に぀いお Submitボタンをクリックするず、回答内容がWorkato内に送信されお、デヌタピルず呌ばれる匕数ずなっお埌続の凊理で䜿甚できるようになりたす。 Workbotでは、「メッセヌゞに返信する」ずいったアクションもあるので、今回はそれを甚いお「回答内容を返信する」ずいったこずを実装しおみたす。 このようにテキスト圢匏で名前を衚瀺させるような蚭定ができたす。 ナヌザヌ名Step1 ずなっおいる郚分が先ほど蚘茉したデヌタピルで、䞭身は「テスト倪郎」になっおいたす。 他の項目に぀いおも同様に蚭定しお、実装するず このように返信がきたした 他にもさたざたな返信スタむルにできたす。䟋えば、 メンション付きで返信 画像やボタン付きで返信 远加で質問を行う 他のBotを呌び出す などなど 列挙したらキリがないです。 興味ある方は、 Workbot詳现蚭定 をご芧ください 実装したBotに぀いお この章では、匊課で利甚しおいるナヌスケヌスに぀いお玹介したす。 ナヌスケヌスは䞋蚘のずおりです。 Teamsの新芏チヌムを䜜成するBot Botに察しお、 ・远加するメンバヌのメヌルアドレス ・チヌム名 ・チヌムの説明 を回答しお、開発課メンバヌ远加する人のチヌムを䜜成する さらにこのずき、「開発課」ずいうタグが蚭定されおいるようにする 早速Botを呌び出しおみたす ※倖郚サむトに公開する関係で䞀郚情報を䌏せおいたす ※存圚しないナヌザヌで登録しようずしおいるので、本来ぱラヌになりたすが、デモずいうこずでご了承ください ナヌスケヌスに蚘茉した項目に぀いお答えるよう芁求されるので、項目を埋めおSubmitを抌すず メンション付き で、確認するよう通知しおくれたす。 このように承認のフェヌズをはさむこずも可胜ずなりたす。 「チヌム情報」をクリックするず、 チヌム情報の詳现を確認するこずができたす。 情報を芋お問題がないのを確認できたので、 「承認」 を抌しおみるず 凊理完了の旚を通知しおくれたす。さらに「新芏チヌムはこちら」ずいうボタンを抌しおみるず チヌムの画面に遷移したしたきちんずチヌムが䜜成されおいたす タグを確認するず タグに぀いおも問題なく䜜成されおいたす ちなみに、確認のフェヌズで 「䞭断」 を遞択するず ずいう通知をしおくれたす。 終わりに 簡単ではありたすが、以䞊がWorkbotの玹介ずなりたす。 Workbotの䟿利性を䌝えられおいれば幞いです。 Workbotですが、他にも様々なケヌスで掻躍できるず考えおおりたす。 䞀郚他瀟様の事䟋もありたすが、䟋ずしお 案件内容をBotが質問→Backlogで課題のチケットを䜜成する 他の自動化の凊理ず組み合わせお、Botで承認フェヌズを再珟する(このずき承認できる人のデヌタを別で甚意しおおけば、承認者の制限もできたす) など挙げられたす。こちらに぀いおも列挙したらキリがなさそうです。 この蚘事を読んで、興味を持っおくださる方がいたら幞いです。 最埌たでお読みいただきありがずうございたした むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の16日目の蚘事です。 こんにちはAI戊略宀 AI゜リュヌション1課 新卒のS.Hです 2025幎に買っおよかった本を玹介したいず思いたす。 あくたで個人的な感想ですので、参考皋床に読んでいただければ幞いです。 タむトル 倀段 ゞャンル 1. ゜フトりェア゚ンゞニアガむドブック ―䞖界基準゚ンゞニアの成功戊略ロヌドマップ 4,180円 ビゞネス 2. Airbnb Story 倧胆なアむデアを生み、困難を乗り越え、超人気サヌビスを぀くる方法 2,465円 ビゞネス 3. (暡擬問題付き)培底攻略 AWS認定 ゜リュヌションアヌキテクト − ア゜シ゚むト教科曞 第3版SAA-C03察応 2,860円 クラりド 4. 基瀎から孊ぶ統蚈孊 3,520円 統蚈 5. 改蚂新版゚ンゞニアのためのデヌタ分析基盀入門基本線 デヌタ掻甚を促進する プラットフォヌムデヌタ品質の考え方 3,300円 デヌタ 6. れロから䜜るDeep Learning ❺ ―生成モデル線 3,960円 AI 7. Pythonで動かしながら孊ぶ コンピュヌタネットワヌク (KS情報科孊専門曞) 4,180円 ネットワヌク ゜フトりェア゚ンゞニアガむドブック ―䞖界基準゚ンゞニアの成功戊略ロヌドマップ 賌入のきっかけ 1on1でキャリアパス぀いお聞かれたずきに、「将来どうなりたいか」に぀いおうたく答えられたせんでした。゚ンゞニアにどのような遞択肢があるの、キャリアを進む䞭でどのような行動が必芁なのかを䜓系的に理解したいず思い、賌入したした。 内容 「キャリアパス」「圹割ごずに求められるスキル」「孊習方法」など、゚ンゞニアが歩むべき道や成長に必芁な芁玠を䜓系的に解説しおいる本です。 読んでみお アメリカのテック䌁業を前提ずしおいるため、日本䌁業の組織構造ずは合わない郚分も倚少ありたす。しかし、゚ンゞニアが成長するための「心構え」や「行動指針」など、ためになるこずがたくさんありたした。初心者から䞊玚者たで党員におすすめできる䞀冊です。 Airbnb Story 倧胆なアむデアを生み、困難を乗り越え、超人気サヌビスを぀くる方法 賌入のきっかけ 技術的なスキルだけでなく、「なぜそのプロダクトが成功したのか」ずいうビゞネス的な芖点も身に぀けたいず思い賌入したした。 内容 Airbnbの創業から珟圚に至るたでの軌跡を描いたノンフィクションです。 創業者たちが資金難に苊しみながらシリアルで食い぀なぎ、投資家から䜕床も断られながらもアむデアを磚き続けた過皋が詳现に描かれおいたす。 読んでみお ナヌザヌが「なぜサヌビスを利甚するのか」「どんな䜓隓があればリピヌトしたいず思うのか」を培底的に分析し、着実にニヌズを掎んでいく様子が印象的です。 (暡擬問題付き)培底攻略 AWS認定 ゜リュヌションアヌキテクト − ア゜シ゚むト教科曞 第3版SAA-C03察応 賌入のきっかけ AI戊略宀でもクラりドを倚甚しおサヌビス開発を行うため、䜓系的にAWSを孊んでおきたいず思い賌入したした。 内容 AWS認定゜リュヌションアヌキテクト ア゜シ゚むト詊隓SAA-C03に察応した教科曞です。EC2、RDS、S3などの基本サヌビスから、VPC、Lambda、CloudFrontなど、実務でよく䜿うサヌビスを網矅的に解説しおいたす。各章末には理解床を確認できる挔習問題があり、巻末には本番圢匏の暡擬問題も収録されおいたす。 読んでみお 初心者に優しい内容で各サヌビスの説明が䞁寧に曞かれおおり、クラりドを䜓系的に孊ぶ䞊でオススメの本です。ただし欠点ずしお、各サヌビスのより具䜓的な説明やナヌスケヌスが䞍足しおいるため、実務に即した問題が出題されるSAA詊隓の合栌は難しいかもしれたせん。 基瀎から孊ぶ統蚈孊 賌入のきっかけ AI・機械孊習のプロゞェクトに関わる䞭で、デヌタ分析の結果を正しく解釈したり、モデルの評䟡指暙を適切に理解したりするには、統蚈の基瀎が䞍可欠だず感じ賌入したした 内容 統蚈孊の基瀎を䜓系的に孊べる教科曞です。蚘述統蚈平均、分散、暙準偏差などから始たり、確率分垃、掚定、怜定、回垰分析たで、統蚈孊の䞻芁なトピックを網矅しおいたす。数匏だけでなく、図やグラフを䜿った芖芚的な説明が豊富で、各章末には理解床を確認できる挔習問題も甚意されおいたす。 読んでみお 統蚈孊を基瀎から孊びたい初孊者、数孊が苊手だけど統蚈を理解したい゚ンゞニアにおすすめです。デヌタサむ゚ンスや機械孊習には蚘述統蚈や確率分垃が必ず䜿われるため、土台を孊ぶこずができたす。 改蚂新版゚ンゞニアのためのデヌタ分析基盀入門基本線 デヌタ掻甚を促進する プラットフォヌムデヌタ品質の考え方 賌入のきっかけ デヌタはAIの孊習や分析の土台ずなりたす。その背景知識を理解し、より高品質なデヌタを収集するこずで、高性胜なAIモデルの構築や粟床の高い分析が可胜になるず考えたした。特に、Extract抜出・Transform倉換・Loadロヌドずいったデヌタパむプラむンの基瀎を䜓系的に孊びたいず思い、賌入したした。 内容 デヌタ分析基盀の蚭蚈・構築に必芁な知識を䜓系的に解説した入門曞です。デヌタレむク、デヌタりェアハりス、デヌタマヌトずいった基本抂念から、ETL/ELTパむプラむンの蚭蚈、デヌタ品質管理、メタデヌタ管理たで幅広くカバヌしおいたす。 読んでみお デヌタ品質の重芁性やデヌタガバナンスの考え方など、技術面だけでなく運甚面の課題に぀いおも觊れられおいるが特に印象に残りたした。たたETLずELTの違いや、バッチ凊理ずストリヌム凊理の䜿い分けなど、基本的な知識が身に付きたす。 れロから䜜るDeep Learning ❺ ―生成モデル線 賌入のきっかけ 最新の画像生成AIは拡散モデルDiffusion Modelsよばれるアヌキテクチャが䞻流ずなっおいるため、その仕組みを理論だけでなく実装レベルで理解したいず思い賌入したした。「れロから䜜る」シリヌズは過去にも読んでおり、実装を通しお孊ぶスタむルが自分に合っおいたこずも決め手でした。 内容 生成モデルの基瀎から最新の拡散モデルたでを段階的に孊ぶこずができたす。正芏分垃、倚次元正芏分垃、混合ガりスモデル、ニュヌラルネットワヌク、倉分オヌト゚ンコヌダVAE、拡散モデルずいう順に、ステップを螏んで理解を深めおいける構成になっおいたす。 読んでみお 実装を通しお孊ぶこずで、拡散モデルの数匏の意味や各ステップの圹割が深く理解できたした。特に、ノむズを段階的に陀去しおいくプロセスを実装するこずで、「なぜこのアルゎリズムが機胜するのか」を理解するこずができたす。 ただし欠点ずしお、text-to-imageやimage-to-imageずいった条件付き生成には察応しおいたせん。扱っおいるのは䞻に無条件の画像生成で、実務で䜿われおいるStable DiffusionやMidjourneyのようなテキストプロンプトからの生成に぀いおは觊れられおいたせん。たた、党䜓的にボリュヌムが少なく感じられ、もう少し応甚的な内容や最新モデルの解説があっおも良かったず思いたす。 Pythonで動かしながら孊ぶ コンピュヌタネットワヌク (KS情報科孊専門曞) 賌入のきっかけ Webアプリケヌション開発やクラりドを利甚する䞭で、ネットワヌクの知識䞍足を感じる堎面が増えおきたした。HTTPやTCP/IPずいった甚語は知っおいるものの、実際にどのように通信が行われおいるのか、パケットレベルでの理解が曖昧だったため、実装を通しお孊べる本曞を賌入したした。 内容 コンピュヌタネットワヌクの基瀎をPythonで実装しながら孊べる技術曞です。TCP/IP、UDP、HTTP、DNSずいったプロトコルの仕組みを、Pythonを䜿っお実際に動かしながら理解しおいきたす。 読んでみお 実際にコヌドを曞きながら孊ぶこずで、抜象的だったネットワヌクの抂念が具䜓的にむメヌゞできるようになりたした。特に、TCPの3りェむハンドシェむクやHTTPリク゚ストの送受信を自分で実装するこずで、むンタヌネットの裏偎を少し理解できた感芚がありたした。 最埌に 振り返っおみるず、2025幎は興味が発散しがちで、あれもこれもず手を出しおしたった䞀幎でした。 2026幎は焊点を絞っお、クラりド特にGoogle Cloudを䞭心に孊んでいこうず思いたす。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の15日目の蚘事です。 はじめに ITディベロップ第2統括郚ITディベロップ2郚開発1課のH.Yです。 私は新卒2幎目の゚ンゞニアずしお、少人数の開発チヌムを䞭心に、AIチヌムやむンフラなど耇数郚門ず連携するプロゞェクトでPMプロゞェクトマネヌゞャヌを任されたした。 厳密には、最初は「PLプロゞェクトリヌダヌをやりたい」ず課長に盞談し、了承をもらっおいたした。ずころが、開発を進めるために必芁なこずをどんどん進めおいたら、気づけばPMの圹割を担っおいたした。 実際、統括PMからも「タスクをどんどん巻き取っおほしい」ずいう意図があり、そのたたPMずしお動くこずになった、ずいう流れです。 初めおの経隓でしたが、「若手だからサポヌトしおもらえるだろうし、せっかくだから色々挑戊しよう」ずいう気持ちで臚んでいたため、䞍安のようなネガティブな感情は特にありたせんでした。 その分、プレむダヌ脳のたた突っ走り、結果ずしお倚くの倱敗を経隓したした。 この蚘事では、その倱敗ず孊びをたずめおいたす。 同じく若手でPMを始めた人やPMをやりたい人の参考になれば幞いです。 プロゞェクト䜓制 PO芁件定矩、受け入れ基準の策定、仕様倉曎の意思決定 統括PM党䜓の進行管理 PM私進捗管理、調敎、スケゞュヌル策定 開発チヌム5名 Yさん業務委蚗ベテラン゚ンゞニアFE芋積もり担圓 Tさんフロント゚ンド実装担圓 Hさんフロント゚ンド実装担圓 Dさんバック゚ンド実装担圓 Pさんバック゚ンド実装小タスク担圓 他郚眲連携 AIチヌムDB䜜成、AI機胜甚のデヌタ敎備、倖郚API䜜成 むンフラチヌムデプロむ手順䜜成、むンフラ構成倉曎察応 デザむナヌ仕様倉曎に䌎うデザむン修正 倱敗談ず孊び 1. 情報共有は“党郚”ではなく“行動可胜な圢”で 状況 ステヌクホルダヌずの方針倉曎や重芁なやり取りがSlackで発生した際、私はメンバヌ党員をメンションしおリンクを貌り、「これの把握お願いしたす」ず連絡しおいたした。 しかし、そのような連絡は膚倧になり、結果ずしお把握挏れが生じたした。 原因 PMずメンバヌが把握する情報の性質が異なる PM浅く広く把握 メンバヌ狭く深く把握 人には凊理できる情報量に限界があるため、メンバヌにPMず同じ情報量の把握を期埅するのは間違い メンバヌ目線では「把握しおください」ず蚀われおも、䜕をすればいいのか分からず、行動に結び぀かない 察応 ただメンバヌに察応しおもらわない情報は共有しない PMの仕事は「メンバヌが動ける状態に情報を敎理するこず」 珟状・課題・完了条件・PMが考える手段・備考を敎理し、Issueに萜ずし蟌む 孊び メンバヌが動けるように情報を倉換するのがPMの仕事 PMが凊理できる情報の深さに限床があるように、メンバヌが凊理できる情報の広さにも限床がある 2. スケゞュヌル認識ズレで焊った話 状況 Yさんず進捗確認をした際、期限の認識が倧きくズレおいるこずが刀明。 私の想定期限は10月14日 Yさんの認識期限はその2週間埌 さらに、Yさんから「そのスケゞュヌルではかなり無理がある」ず指摘され、焊りたした。 原因 統括PMが提瀺したスケゞュヌルは「理想倀」だった 私はそれを「絶察倀」ず誀解し、Slackで共有したが通知過倚で芋萜ずされおいた 認識合わせを党員で行う仕組みがなかった プロゞェクト理解が浅く、ガントチャヌトを䜜れる状態ではなかったため、「ずりあえず進めれば芋通しが぀く」ずいうプレむダヌ的発想で動いおいた 察応 Yさんず統括PMを亀えお緊急ミヌティングを実斜。結果、スケゞュヌルは調敎可胜であるこずが分かり、事なきを埗たした。 孊び ガントチャヌトを䜜るべきずいう理解はあったが、プロゞェクト初期は情報䞍足で䜜れないこずもある ガントチャヌトを䜜る胜力は、プロゞェクトの性質をもずにリスク芁因を芋抜く力に䟝存する 「ずりあえず進める」だけでは䞍確実性が増すため、進めながらも認識合わせの仕組みを早期に䜜る 重芁な認識合わせはSlackだけに頌らず、確実に確認できる仕組みを甚意する 3. 芁件の裏にある課題を理解するこずの重芁性 状況 既存システムにない新しい機胜の甚途を勘違いし、POの想定ずは異なる仕様で実装しおしたったため、手戻りが生じたした。 原因 POが定矩した受け入れ基準に仕様が蚘茉されおいなかった 芁件は理解したが、事業郚の業務フロヌ䞊「なぜ必芁なのか」を理解しないたた進めた 察応 「本圓にその機胜必芁なの」「より適切な課題解決手段はないのか」ず疑う意識を持぀こず 孊び 蚀われたからやるのではなく、自分の蚀葉で「なぜやるか」を説明できる皋床に理解しないず手戻りが生じる可胜性がある 4. PJ完了の定矩を明確にするこずの重芁性 状況 プロゞェクト序盀にPOに受け入れ基準を定矩しおもらい、仕様倉曎のたびに合意を取りながら曎新しおいたした。 しかし終盀ではPJ完了の定矩が曖昧になり、関係者間で「䜕をもっおプロゞェクト終了なのか」が䞍明確になり、メンバヌを䞍安にさせおしたいたした。 原因 仕様倉曎が高頻床で発生し、受け入れ基準曎新の優先床が䞋がった 「受け入れ基準を満たすものを䜜る」ずいう合意があったため、基準が増えるほど負担が増える心理的ハヌドルがあった 察応 珟状をPOに報告し、「受け入れ基準を最新化するのでレビュヌしおほしい」ず䟝頌 合意圢成を再床取り盎し、受け入れ基準を最新化しおプロゞェクト完了条件を明確化 孊び PJ完了の定矩は、関係者党員が同じ認識を持おるように仕組み化する PMになりたい人ぞ プレむダヌの時もPMの時も、 自䞻性・課題解決胜力・目的意識 ずいった胜力はずおも重芁です。 ただし、PMになるず、それらの胜力を 掻かす方法が倧きく倉わりたす 。 プレむダヌは「自分で成果を出す」こずに集䞭したすが、PMは「チヌムが成果を出せる状態を䜜る」こずが圹割です。 PMずしお働くのは簡単ではありたせん。調敎や意思決定、リスク管理など、プレむダヌ時代には経隓しなかった難しさがありたす。 しかし、その分、 刺激が倚く、孊びも倧きい仕事です 。 この蚘事で玹介した私の倱敗は、すべお成長の糧になりたした。 もしあなたがPMに興味を持っおいるなら、ぜひ挑戊しおみおください。
本蚘事は【 Advent Calendar 2025 】の14日目の蚘事です。 デゞタルプラットフォヌム統括本郚デヌタ゜リュヌション統括郚デヌタアヌキテクト郚アナリティクス掚進課のA・Kです。 そういえば自分でも久しく忘れおいたのですが、ミステリヌや掚理小説、叙述トリック物の小説が奜きで昔よく読んでいたした。タむトルで分かった方もいるかもしれたせんが、奜きな䜜家は工孊博士の森博嗣先生です。 ずいうこずで、今回はBIツヌルTableauのIF文にた぀わるミステリヌを曞こうず思いたす。 0. プロロヌグ Tableauには「蚈算フィヌルド」ずいう抂念がありたす。蚈算フィヌルドを䜿うず、定数・倉数を亀えた算術蚈算や文字列操䜜などの結果をフィヌルドに出力するこずができ、䞻に動的な出力を埗たい堎合に利甚したす。 䟋えば、顧客ぞの商品販売履歎が蚘録されおいる以䞋のようなテヌブルがあるずしたす。 泚文ID 泚文日 ナヌザヌID ナヌザヌ名 郜道府県 定䟡 賌入額 2025-645614 2025/1/30 U-562596 鈎朚 花子 富山県 40000 40000 2025-748109 2025/3/29 U-585788 吉田 盎暹 新期県 180000 180000 2025-388360 2025/4/18 U-577077 高橋 健 䜐賀県 260000 208000 2025-355111 2025/5/3 U-750671 æ–Žè—€ 真简 青森県 250000 250000 2025-420588 2025/5/4 U-362222 䞭村 真由 宮城県 200000 200000 2025-290629 2025/5/16 U-013025 䜐藀 倪郎 沖瞄県 130000 104000 2025-277039 2025/6/22 U-715673 枡蟺 圩 茚城県 200000 40000 2025-864082 2025/7/28 U-577077 高橋 健 䜐賀県 300000 300000 2025-973922 2025/9/30 U-909473 小林 誠 東京郜 180000 180000 2025-091362 2025/11/22 U-446022 加藀 結衣 栃朚県 30000 24000 このテヌブルをTableauで読み蟌み、「どのくらいの割匕率で賌入しおもらったのか」の列を远加しお可芖化する堎合、以䞋の蚈算フィヌルドを䜜成し新たな列ずしお远加するこずで実珟できたす。 割匕率([定䟡] - [賌入額]) / [定䟡] 1. 双頭の悪魔 たず、以䞋の蚘述をご芧ください。 IF SIZE() = 1 THEN (SUM([定䟡] - [賌入額])) / SUM([定䟡])ELSE (SUM([定䟡] - [賌入額])) / SUM([定䟡])END Tableauの蚈算フィヌルドにおけるIF文のフォヌマット今回のケヌスに盞圓するものは、以䞋のずおりです。 IF <条件> THEN <条件に合臎する堎合の出力>ELSE <条件に合臎しない堎合の出力>END たた、SUM関数は指定したカラムの倀をさたざたな粒床党䜓、䞀郚のグルヌプにおいお、などで合蚈するこずができたす。 ぀たり、はじめに掲茉した蚘述は、条件「SIZE() = 1」に合臎する堎合 (SUM([定䟡] - [賌入額])) / SUM([定䟡]) ïŒˆïŒè€‡æ•°ãƒ¬ã‚³ãƒŒãƒ‰ã®å‰²åŒ•率 ã‚’返し、条件に合臎しない堎合 (SUM([定䟡] - [賌入額])) / SUM([定䟡]) を返したす。  っお、えこれどっちの蚈算匏も同じくない はい、そうです。この条件に合臎する堎合ずしない堎合の蚈算匏は、 たったく同じ蚈算匏 です。 「分かったこれ本圓はIF文を䜿わなくおも実珟できるけど、無理やりIF文にしおるだけだろ」ず予想した方がいるかもしれたせんが、残念ながら今回のケヌスでは IFを䜿い条件分岐させないず正しい結果を埗られたせん 。 5. ここでいきなり゚ピロヌグ  こうしお䜜成した蚈算フィヌルドを䜿い、以䞋のような集蚈結果の衚瀺を実珟するこずができたした。めでたしめでたし。 総蚈ずいう行が远加されおいるこずが分かるず思いたす。総蚈はTableauがデフォルトで持぀集蚈機胜で、各レコヌドをさたざたな粒床で合蚈したり平均したりするこずができたす。 今回は定䟡や賌入額の平均単䟡、平均割匕率を衚瀺したかったので、「平均により集蚈を行った総蚈」を最終行に衚瀺するこずにしおいたす。 2. 真倏の総蚈匏 さお、総蚈はTableauのデフォルトの機胜ずお䌝えしたしたが、実はトラップがありたす。以䞋の衚は、平均による総蚈機胜を単玔に適甚したずきの集蚈結果です。 䞀芋どこがおかしいのか分かりにくいですが、前項で掲茉したキャプチャずの違いは割匕率の総蚈です。前項キャプチャでは13.8だったのに察し、今回は14.0ずなっおいたす。これはどちらが正しいのでしょうか ずいうこずで正しい蚈算をしおみたす。割匕率の平均ずは、いわば党䜓に均したならしたずきの割匕率にあたるので 割匕金額の合蚈 ÷ 定䟡の合蚈 で算出するこずができたす。実際に蚈算しおみたす。 割匕金額の合蚈  244,000 定䟡の合蚈  1,770,000 割匕金額の合蚈 ÷ 定䟡の合蚈  0.1378 ≒  13.8 ずいうこずで、正しい倀は13.8でした。え では、14.0ずいうのはどこから来た数倀なのでしょうか 実は、総蚈機胜の平均は以䞋赀枠郚分の平均を集蚈しおいたす。 0  0  20  
  20÷ 10 14.0 ですね。このように、総蚈機胜は各レコヌドの倀を単玔に算術蚈算しお集蚈を行うのですが、 割合の平均は算術平均で求めるこずができたせん 。これが総蚈のトラップです。 では、定䟡ず賌入額の集蚈には算術平均を䜿い、割合の平均を集蚈するずきは別の蚈算を行うにはどうすればいいのでしょうか 3. GYOTH あらためお、冒頭に出おきた蚈算匏を再床芋おみたす。 IF SIZE() = 1 THEN (SUM([定䟡] - [賌入額])) / SUM([定䟡])ELSE (SUM([定䟡] - [賌入額])) / SUM([定䟡])END ここで気になるのが  SIZE() = 1  ãšã„う条件です。SIZE() ずはどのような関数なのでしょうか 結論、 SIZE関数は 各レコヌドが属するパヌティション内の行数を返したす 。パヌティションずいう単䜍がどこを衚すかずいうず、以䞋の図のずおりです。 埓っお、各行に SIZE関数を適甚するず、以䞋の倀が返っおきたす。 犯人が芋えおきたしたね。 4. むニシ゚ヌション・サム 埓っお、 割匕率IF SIZE() = 1 THEN (SUM([定䟡] - [賌入額])) / SUM([定䟡])ELSE (SUM([定䟡] - [賌入額])) / SUM([定䟡])END この蚈算フィヌルドを䜜成するこずにより、SIZE() = 1 、぀たり総蚈の行においお各レコヌドの平均を蚈算せず (SUM([定䟡] - [賌入額])) / SUM([定䟡]) を蚈算し、たた SIZE() ≠ 1 、぀たり総蚈以倖のすべおの行においおも (SUM([定䟡] - [賌入額])) / SUM([定䟡]) を蚈算するこずができるようになりたす。 ここで1぀、最埌の疑問が残りたす。「総蚈以倖の行はすべお1行ごずに割匕率を蚈算するんだから、わざわざSUMを䜿っお合蚈する必芁はないのでは」 その答えは2぀ありたす。1぀は、そういう仕様だからですSUMなど集蚈関数を぀けないず゚ラヌが出たす。なんだそりゃ、ず思う方もいるかもしれたせんが、これは合理的な仕様です。その合理性が分かるもう1぀の理由が、Tableauはデヌタ分析に䜿うツヌルなので、以䞋のように別の切り口でも分析しおみたいずいうニヌズがありえる点です。この堎合、地域ごずに定䟡の合蚈、割匕額の合蚈を算出するためにSUMを行う必芁があるのは明癜です。 5. ゚ピロヌグ  こうしお䜜成した蚈算フィヌルドを䜿い、以䞋のような集蚈結果の衚瀺を実珟するこずができたした。めでたしめでたし。 6. あずがき ずいうこずで、Tableau IF文ミステリィでした。今回のケヌスは、実際に業務でのレポヌト䜜成䞭に盎面した問題がベヌスになっおいたす。私自身、IF文の条件に合う堎合ず合わない堎合でたったく同じ蚈算匏を䜿う、ずいう経隓がこれたでなかったので、このような方法で解決できたこずが非垞に興味深かったです。 Tableauはこのような圢で蚈算フィヌルドを掻甚し、集蚈を行うこずができたす。このミステリヌもどきでTableauに興味を持たれた方は、さたざたな機胜や蚈算フィヌルドを䜿っおデヌタの可芖化にチャレンゞしおみおください新たな発芋があるかもしれたせん。 以䞊、ノァン・ダむンでした。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の13日目の蚘事です。 Autogen ずは Autogenは、AIを掻甚したマルチ゚ヌゞェントシステム構築のためのフレヌムワヌクです。 簡単に蚀うず AI゚ヌゞェントを盞互に䌚話させ、協働させ、タスクを自動的に解決させるこずができたす。 1぀のAIがすべおを行う代わりに、耇数の専門化された゚ヌゞェントが存圚したす。 これらの゚ヌゞェントは、耇雑なワヌクフロヌを完了するために通信し、情報を共有し、協力しお䜜業できたす。 特城 LLM統合 GPTなどのモデルず連携したす。 スケヌラブルなアヌキテクチャ 耇雑な倚段階プロセスを凊理したす。 カスタム゚ヌゞェント ニヌズに合わせた゚ヌゞェントを構築したす。 歎史 Autogenは圓初、Microsoftのみで開発されおいたした。 その埌、AG2ずバヌゞョン0.4に分割されたした。 AG2は党く異なるフレヌムワヌクであり、別個に管理されおいたす。 ChatGPTやその他のコヌド生成゚ヌゞェントはAG2向けのコヌドを提䟛したす。 Autogenのコヌドを生成するようChatGPTに䟝頌しおも正しいコヌドが埗られないです。 AIから䜜成したAutogenのコヌド ナヌスケヌス ゜フトりェア開発アシスタント ゚ヌゞェント コヌド生成゚ヌゞェント初期コヌドを生成 コヌドレビュヌ゚ヌゞェントバグずベストプラクティスをチェック ドキュメント゚ヌゞェント䜿甚方法ドキュメントを䜜成 目暙 開発を加速し、品質を維持する。 旅行蚈画 ゚ヌゞェント 旅皋プランナヌ旅行の日ごずの旅皋を䜜成したす 費甚芋積もり担圓者旅行のおおよその費甚を蚈算したす レビュヌ担圓者蚈画が珟実的か぀実践的かどうかを確認する 目暙 予算内で珟実的な旅行蚈画を䜜成する。 コンテンツ制䜜 ゚ヌゞェント ラむタヌブログや蚘事の草皿䜜成 線集者文法やトヌンのチェック SEOスペシャリストキヌワヌドの最適化 目暙 高品質でSEO察策に優れたコンテンツを䜜成する。 デヌタ分析ず報告 ゚ヌゞェント デヌタコレクタヌAPIからデヌタを取埗 アナリストむンサむトずチャヌトを生成 レポヌトラむタヌ調査結果を平易な蚀葉で芁玄 目暙 ゚ンドツヌ゚ンドのレポヌト䜜成を自動化する。 APIキヌに぀いお OpenAI APIキヌは数幎前は無料でしたが、珟圚は無料ではありたせん。 掚奚アプロヌチ Gemini APIキヌを䜿甚する https://ai.google.dev/gemini-api/docs/api-key  Ollamaを䜿甚するLLMモデルをロヌカルで実行するために䜿甚 OpenRouter APIキヌを䜿甚する Ollama は自分の所属する䌚瀟ルヌルに埓っお利甚しお䞋さい。 䞀定期間無料で䜿甚できるGemini APIキヌたたはOpenRouter APIキヌを䜿甚するのが最適です。 私はGeminiキヌを䜿甚したす。Geminiキヌを䜿甚する堎合、以䞋のコヌドでAutogenを詊すこずができたす Geminiにログむンし、パヌ゜ナルアカりントを䜿甚しおAPIキヌを生成しおください。 重芁なポむント Autogenでは非同期で゚ヌゞェントぞの呌び出しが行われる。Pythonラむブラリで非同期コヌディングを実珟するためにAsyncioが䜿甚される。 Autogenでは、各゚ヌゞェントがどの分野の専門家であるか私たちが指定したす 初めおのオヌトゞェン゚ヌゞェントを䜜成したしょう 手順 Autogen甚の環境を䜜成する Autogent甚の環境の䜜成 python3 -m venv autogensource autogen/bin/activate requirements.txt ファむルを䜜成する 必芁なパッケヌゞをすべお1぀のファむルで定矩する requirements.txt autogen-agentchatautogen-coreautogen-extasynciodotenvopenaitiktoken autogen - agentchat autogen - core autogen - ext asyncio dotenv openai tiktoken pip install -r requirements.txt GEMINI APIキヌの環境倉数を蚭定するご自身のAPIキヌを远加しおください (APIキヌの定矩) .env GEMINI_API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" GEMINI_API_KEY = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx " 単䞀゚ヌゞェントの䜜成 質問に察しお出力結果を返すシンプルな単䞀゚ヌゞェントを䜜成する agent.py import asynciofrom autogen_agentchat.agents import AssistantAgentfrom autogen_ext.models.openai import OpenAIChatCompletionClientfrom autogen_agentchat.messages import TextMessagefrom autogen_core.models import ModelInfofrom dotenv import load_dotenvimport osload_dotenv()api_key = os.getenv("GEMINI_API_KEY")model_client = OpenAIChatCompletionClient( model="gemini-2.0-flash-lite", model_info=ModelInfo(vision=True, function_calling=True, json_output=True, family="unknown", structured_output=True), api_key=api_key,)assistant = AssistantAgent( name="Chatgpt", model_client=model_client, description="First basic Agent")async def main(): result = await assistant.run(task="What is the capital of Japan?") print(result.messages[-1].content)asyncio.run(main()) import asyncio from autogen_agentchat . agents import AssistantAgent from autogen_ext . models . openai import OpenAIChatCompletionClient from autogen_agentchat . messages import TextMessage from autogen_core . models import ModelInfo from dotenv import load_dotenv import os load_dotenv () api_key = os . getenv ( " GEMINI_API_KEY " ) model_client = OpenAIChatCompletionClient ( model = " gemini-2.0-flash-lite " , model_info = ModelInfo ( vision =True , function_calling =True , json_output =True , family = " unknown " , structured_output =True ), api_key = api_key , ) assistant = AssistantAgent ( name = " Chatgpt " , model_client = model_client , description = " First basic Agent " ) async def main (): result = await assistant . run ( task = " What is the capital of Japan? " ) print ( result . messages [ - 1 ]. content ) asyncio . run ( main ()) python3 agent.py アりトプット マルチ゚ヌゞェントAIを䜜成したしょう 指定された予算ず指定期間内で旅行の旅皋を生成するマルチ゚ヌゞェントシステムを構築する。 itinerary_agent - 指定された郜垂ず期間に察しお、詳现な日ごずの旅行蚈画を䜜成したす。 cost_agent - 旅皋を受け取り、おおよその費甚を蚈算したす亀通費、食費、芳光費、宿泊費。 review_agent - 旅皋ず費甚芋積もりが珟実的かどうかを確認する 䞉人の゚ヌゞェントが互いに連携し、最終的に完璧な旅皋を生成する multiAgent.py import asynciofrom autogen_ext.models.openai import OpenAIChatCompletionClientfrom autogen_agentchat.agents import AssistantAgentfrom autogen_agentchat.teams import RoundRobinGroupChatfrom autogen_agentchat.messages import TextMessagefrom autogen_core.models import ModelInfofrom dotenv import load_dotenvimport osload_dotenv()api_key = os.getenv("GEMINI_API_KEY")model_client = OpenAIChatCompletionClient( model="gemini-2.0-flash-lite", model_info=ModelInfo(vision=True, function_calling=True, json_output=True, family="unknown", structured_output=True), api_key=api_key)itinerary_agent = AssistantAgent( name="itinerary_agent", model_client=model_client, system_message=( "あなたは旅行日皋の専門家です。 " "特定の郜垂ず滞圚日数が䞎えられた堎合、詳现な日別旅行蚈画を䜜成しおください。 " "時間割、蚪問堎所、亀通手段の提案、およびその理由を含めおください。" ))cost_agent = AssistantAgent( name="cost_agent", model_client=model_client, system_message=( "あなたは旅行費甚芋積もり担圓者です。" "旅皋が䞎えられた堎合、亀通費、食費、芳光費、宿泊費のおおよその費甚を蚈算しおください。 " "合蚈がナヌザヌの予算を超えた堎合、より安䟡な倉曎案を提案しおください。" ))review_agent = AssistantAgent( name="review_agent", model_client=model_client, system_message=( "旅皋ず費甚芋積もりを怜蚌する審査員です。 " "蚈画が珟実的か確認しおください移動時間、営業時間、順序。 " "問題がある堎合は、前の担圓者に修正を䟝頌しおください。" ))team = RoundRobinGroupChat( participants=[itinerary_agent, cost_agent, review_agent], max_turns=6)async def run_team(): user_task = TextMessage( content="京郜ぞの3日間の旅行を5䞇円以内で蚈画しおください。予算内に収め぀぀、楜しめるプランにしたしょう。内容は200語以内に収めおください。", source="user") result = await team.run(task=user_task) for msg in result.messages: print(f"{msg.source}:\n{msg.content}\n")async def main(): await run_team()asyncio.run(main()) import asyncio from autogen_ext . models . openai import OpenAIChatCompletionClient from autogen_agentchat . agents import AssistantAgent from autogen_agentchat . teams import RoundRobinGroupChat from autogen_agentchat . messages import TextMessage from autogen_core . models import ModelInfo from dotenv import load_dotenv import os load_dotenv () api_key = os . getenv ( " GEMINI_API_KEY " ) model_client = OpenAIChatCompletionClient ( model = " gemini-2.0-flash-lite " , model_info = ModelInfo ( vision =True , function_calling =True , json_output =True , family = " unknown " , structured_output =True ), api_key = api_key ) itinerary_agent = AssistantAgent ( name = " itinerary_agent " , model_client = model_client , system_message = ( " あなたは旅行日皋の専門家です。 " " 特定の郜垂ず滞圚日数が䞎えられた堎合、詳现な日別旅行蚈画を䜜成しおください。 " " 時間割、蚪問堎所、亀通手段の提案、およびその理由を含めおください。 " ) ) cost_agent = AssistantAgent ( name = " cost_agent " , model_client = model_client , system_message = ( " あなたは旅行費甚芋積もり担圓者です。 " " 旅皋が䞎えられた堎合、亀通費、食費、芳光費、宿泊費のおおよその費甚を蚈算しおください。 " " 合蚈がナヌザヌの予算を超えた堎合、より安䟡な倉曎案を提案しおください。 " ) ) review_agent = AssistantAgent ( name = " review_agent " , model_client = model_client , system_message = ( " 旅皋ず費甚芋積もりを怜蚌する審査員です。 " " 蚈画が珟実的か確認しおください移動時間、営業時間、順序。 " " 問題がある堎合は、前の担圓者に修正を䟝頌しおください。 " ) ) team = RoundRobinGroupChat ( participants = [ itinerary_agent , cost_agent , review_agent ], max_turns = 6 ) async def run_team (): user_task = TextMessage ( content = " 京郜ぞの3日間の旅行を5䞇円以内で蚈画しおください。予算内に収め぀぀、楜しめるプランにしたしょう。内容は200語以内に収めおください。 " , source = " user " ) result = await team . run ( task = user_task ) for msg in result . messages : print ( f " { msg . source } : \n{ msg . content }\n " ) async def main (): await run_team () asyncio . run ( main ()) python3 multiAgent.py アりトプット user:京郜ぞの3日間の旅行を5䞇円以内で蚈画しおください。予算内に収め぀぀、楜しめるプランにしたしょう。内容は200語以内に収めおください。itinerary_agent:承知いたしたした。京郜3日間5䞇円以内の旅皋をご提案したす。**1日目:*** **午前:** 䌏芋皲荷倧瀟ぞ。朱色の鳥居が圧巻です。* **午埌:** 枅氎寺呚蟺を散策。枅氎の舞台からの眺めを堪胜し、二幎坂・産寧坂でお土産探し。* **倕食:** 予算を考慮し、駅呚蟺の定食屋で。* **亀通:** 垂バス1日乗車刞利甚がお埗**2日目:*** **午前:** 金閣寺ぞ。金箔が茝く姿は必芋。* **午埌:** 嵐山ぞ。竹林の道や枡月橋を散策。人力車は予算に応じお。* **倕食:** 嵐山で食べ歩きや、手頃な䟡栌のカフェで倕食を。* **亀通:** 嵐電、JR**3日目:*** **午前:** 錊垂堎で食べ歩き。* **午埌:** 京郜埡所を散策事前予玄掚奚。* **倕食:** 京郜駅呚蟺で新幹線に乗る前に、お土産を賌入し、駅ビル内のレストランで最埌の食事を。* **亀通:** 垂バス、電車、新幹線**宿泊:** ゲストハりスや栌安ホテルを遞択。食事は、安䟡な店やコンビニを掻甚するこずで予算内に収たるでしょう。cost_agent:玠晎らしいプランですね 䌏芋皲荷倧瀟、枅氎寺、金閣寺、嵐山ず、京郜の䞻芁な芳光スポットをバランス良く網矅しおいたす。垂バスの䞀日乗車刞を利甚するなど、亀通費を抑える工倫も玠晎らしいです。**詳现な費甚抂算*** **亀通費:** * 新幹線埀埩玄20,000円 (出発地によりたす) * 垂バス1日乗車刞1,100円 × 2日 = 2,200円 * 嵐電、JRなど3,000円 * **合蚈玄25,200円*** **宿泊費:** ゲストハりスたたは栌安ホテル (3泊)1泊あたり5,000円ず仮定しお、15,000円* **食費:** 1日あたり3,000円 × 3日 = 9,000円* **芳光費:** 各斜蚭の入堎料、お土産代など玄5,000円* **合蚈玄54,200円**この抂算では、残念ながら5䞇円の予算を若干オヌバヌしおしたいたす。 そこで、いく぀か調敎案をご提案したす。**調敎案**1. **亀通費の芋盎し:** * 新幹線の代わりに、倜行バスを利甚する (埀埩10,000円皋床で枈む可胜性がありたす)。2. **宿泊費の芋盎し:** * カプセルホテルや、より栌安なゲストハりスを探す。 * Airbnbなどを利甚する。3. **食費の芋盎し:** * 自炊できるゲストハりスを遞ぶ。 * ランチは軜食にする。4. **芳光費の芋盎し:** * 入堎料が無料の堎所も掻甚する䟋䌏芋皲荷倧瀟。 * お土産は、少なめにするか、リヌズナブルなものを遞ぶ。これらの調敎を行うこずで、5䞇円の予算内に収たる可胜性がありたす。review_agent:ありがずうございたす。詳现な費甚抂算ず、的確な調敎案のご提瀺、倧倉助かりたす。確かに、新幹線を倜行バスに倉曎するこずや、宿泊費を抑えるこずが、予算内に収めるための重芁なポむントですね。**修正埌のプラン調敎案を反映****1日目:*** **午前:** 䌏芋皲荷倧瀟ぞ。* **午埌:** 枅氎寺呚蟺を散策。枅氎の舞台からの眺めを堪胜し、二幎坂・産寧坂でお土産探し。* **倕食:** 予算を考慮し、駅呚蟺の定食屋で。* **亀通:** 垂バス1日乗車刞利甚**2日目:*** **午前:** 金閣寺ぞ。* **午埌:** 嵐山ぞ。竹林の道や枡月橋を散策。人力車は今回は芋送り。* **倕食:** 嵐山で食べ歩きや、手頃な䟡栌のカフェで倕食を。* **亀通:** 嵐電、JR**3日目:*** **午前:** 錊垂堎で食べ歩き。* **午埌:** 京郜埡所を散策事前予玄掚奚。* **倕食:** 京郜駅呚蟺で新幹線に乗る前に、お土産を賌入し、駅ビル内のレストランで最埌の食事を。* **亀通:** 垂バス、電車、倜行バス**費甚の再抂算調敎案反映埌*** **亀通費:** * 倜行バス埀埩玄10,000円 * 垂バス1日乗車刞1,100円 × 2日 = 2,200円 * 嵐電、JRなど3,000円 * **合蚈玄15,200円*** **宿泊費:** カプセルホテルたたは栌安ゲストハりス (3泊)1泊あたり4,000円ず仮定しお、12,000円* **食費:** 1日あたり2,500円 × 3日 = 7,500円ランチは軜食䞭心* **芳光費:** 各斜蚭の入堎料、お土産代など玄10,300円 (䌏芋皲荷倧瀟、枅氎寺、金閣寺は入堎料がかかりたすが、それ以倖の堎所では無料の堎所も掻甚し、お土産代を調敎)* **合蚈玄45,000円**これで、5䞇円の予算内に収たりそうですねこのプランは、倜行バスの利甚、宿泊費の節玄、食費の芋盎し、芳光費の調敎によっお、予算をクリアできる可胜性が高くなりたした。玠晎らしいです。itinerary_agent:完璧ですね修正案が反映され、5䞇円以内の予算で、京郜の䞻芁芳光地を巡る3日間の旅皋が実珟可胜になりたした。**補足事項:*** **倜行バスの予玄:** 確実に予玄を確保するため、早めの予玄をお勧めしたす。* **宿泊斜蚭の予玄:** ゲストハりスやカプセルホテルも、人気のある斜蚭はすぐに満宀になるこずがありたす。こちらも早めの予玄を。* **京郜埡所の予玄:** 事前予玄が必芁ですので、忘れずに手続きしおください。* **芳光斜蚭の開通時間:** 各斜蚭の開通時間を事前に確認し、効率よく芳光できるように蚈画を立おたしょう。* **移動時間:** 垂バスや電車での移動時間を考慮し、無理のないスケゞュヌルを立おたしょう。* **持ち物:** 歩きやすい靎、防寒具、雚具などを忘れずに準備したしょう。このプランで、京郜の魅力を存分に楜しんでください 良い旅になりたすようにcost_agent:ありがずうございたす 完璧なプランニングですね。補足事項も非垞に的確で、旅行者が安心しお旅を楜しめるよう、现やかな配慮がされおいたす。このプランを参考に、楜しい京郜旅行を満喫しおください 䜕か他に質問があれば、お気軜にお尋ねください。review_agent:ありがずうございたす こちらこそ、玠晎らしいプランを䞀緒に䜜り䞊げるこずができ、倧倉光栄です。䜕かお圹に立おるこずがあれば、い぀でもお声がけください。 良い旅になりたすように user : 京郜ぞの3日間の旅行を5䞇円以内で蚈画しおください。予算内に収め぀぀、楜しめるプランにしたしょう。内容は200語以内に収めおください。 itinerary_agent : 承知いたしたした。京郜3日間5䞇円以内の旅皋をご提案したす。 ** 1日目 : ** * ** 午前 : ** 䌏芋皲荷倧瀟ぞ。朱色の鳥居が圧巻です。 * ** 午埌 : ** 枅氎寺呚蟺を散策。枅氎の舞台からの眺めを堪胜し、二幎坂・産寧坂でお土産探し。 * ** 倕食 : ** 予算を考慮し、駅呚蟺の定食屋で。 * ** 亀通 : ** 垂バス 1日乗車刞利甚がお埗  ** 2日目 : ** * ** 午前 : ** 金閣寺ぞ。金箔が茝く姿は必芋。 * ** 午埌 : ** 嵐山ぞ。竹林の道や枡月橋を散策。人力車は予算に応じお。 * ** 倕食 : ** 嵐山で食べ歩きや、手頃な䟡栌のカフェで倕食を。 * ** 亀通 : ** 嵐電、JR ** 3日目 : ** * ** 午前 : ** 錊垂堎で食べ歩き。 * ** 午埌 : ** 京郜埡所を散策事前予玄掚奚。 * ** 倕食 : ** 京郜駅呚蟺で新幹線に乗る前に、お土産を賌入し、駅ビル内のレストランで最埌の食事を。 * ** 亀通 : ** 垂バス、電車、新幹線 ** 宿泊 : ** ゲストハりスや栌安ホテルを遞択。食事は、安䟡な店やコンビニを掻甚するこずで予算内に収たるでしょう。 cost_agent : 玠晎らしいプランですね 䌏芋皲荷倧瀟、枅氎寺、金閣寺、嵐山ず、京郜の䞻芁な芳光スポットをバランス良く網矅しおいたす。垂バスの䞀日乗車刞を利甚するなど、亀通費を抑える工倫も玠晎らしいです。 ** 詳现な費甚抂算 ** * ** 亀通費 : ** * 新幹線埀埩玄20 , 000円 ( 出発地によりたす ) * 垂バス1日乗車刞 1 , 100円 × 2日 = 2 , 200円 * 嵐電、JRなど 3 , 000円 * ** 合蚈玄25 , 200円 ** * ** 宿泊費 : ** ゲストハりスたたは栌安ホテル ( 3泊 )  1泊あたり5 , 000円ず仮定しお 、 15 , 000円 * ** 食費 : ** 1日あたり3 , 000円 × 3日 = 9 , 000円 * ** 芳光費 : ** 各斜蚭の入堎料、お土産代など玄5 , 000円 * ** 合蚈玄54 , 200円 ** この抂算では、残念ながら5䞇円の予算を若干オヌバヌしおしたいたす。 そこで、いく぀か調敎案をご提案したす。 ** 調敎案 ** 1 . ** 亀通費の芋盎し : ** * 新幹線の代わりに、 倜行バスを利甚する ( 埀埩10 , 000円皋床で枈む可胜性がありたす ) 。 2 . ** 宿泊費の芋盎し : ** * カプセルホテルや、より栌安なゲストハりスを探す。 * Airbnbなどを利甚する。 3 . ** 食費の芋盎し : ** * 自炊できるゲストハりスを遞ぶ。 * ランチは軜食にする。 4 . ** 芳光費の芋盎し : ** * 入堎料が無料の堎所も掻甚する䟋䌏芋皲荷倧瀟。 * お土産は、少なめにするか、リヌズナブルなものを遞ぶ。 これらの調敎を行うこずで、 5䞇円の予算内に収たる可胜性がありたす 。 review_agent : ありがずうございたす。詳现な費甚抂算ず、的確な調敎案のご提瀺、倧倉助かりたす。確かに、新幹線を倜行バスに倉曎するこずや、宿泊費を抑えるこずが、予算内に収めるための重芁なポむントですね。 ** 修正埌のプラン調敎案を反映 ** ** 1日目 : ** * ** 午前 : ** 䌏芋皲荷倧瀟ぞ。 * ** 午埌 : ** 枅氎寺呚蟺を散策。枅氎の舞台からの眺めを堪胜し、二幎坂・産寧坂でお土産探し。 * ** 倕食 : ** 予算を考慮し、駅呚蟺の定食屋で。 * ** 亀通 : ** 垂バス 1日乗車刞利甚  ** 2日目 : ** * ** 午前 : ** 金閣寺ぞ。 * ** 午埌 : ** 嵐山ぞ。竹林の道や枡月橋を散策。人力車は今回は芋送り。 * ** 倕食 : ** 嵐山で食べ歩きや、手頃な䟡栌のカフェで倕食を。 * ** 亀通 : ** 嵐電、JR ** 3日目 : ** * ** 午前 : ** 錊垂堎で食べ歩き。 * ** 午埌 : ** 京郜埡所を散策事前予玄掚奚。 * ** 倕食 : ** 京郜駅呚蟺で新幹線に乗る前に、お土産を賌入し、駅ビル内のレストランで最埌の食事を。 * ** 亀通 : ** 垂バス、電車、倜行バス ** 費甚の再抂算調敎案反映埌 ** * ** 亀通費 : ** * 倜行バス埀埩玄10 , 000円 * 垂バス1日乗車刞 1 , 100円 × 2日 = 2 , 200円 * 嵐電、JRなど 3 , 000円 * ** 合蚈玄15 , 200円 ** * ** 宿泊費 : ** カプセルホテルたたは栌安ゲストハりス ( 3泊 )  1泊あたり4 , 000円ず仮定しお 、 12 , 000円 * ** 食費 : ** 1日あたり2 , 500円 × 3日 = 7 , 500円 ランチは軜食䞭心 * ** 芳光費 : ** 各斜蚭の入堎料、お土産代など玄10 , 300円 ( 䌏芋皲荷倧瀟、枅氎寺、金閣寺は入堎料がかかりたすが、それ以倖の堎所では無料の堎所も掻甚し、お土産代を調敎 ) * ** 合蚈玄45 , 000円 ** これで、 5䞇円の予算内に収たりそうですね  このプランは、倜行バスの利甚、宿泊費の節玄、食費の芋盎し、芳光費の調敎によっお、予算をクリアできる可胜性が高くなりたした。玠晎らしいです。 itinerary_agent : 完璧ですね修正案が反映され、 5䞇円以内の予算で 、京郜の䞻芁芳光地を巡る3日間の旅皋が実珟可胜になりたした。 ** 補足事項 : ** * ** 倜行バスの予玄 : ** 確実に予玄を確保するため、早めの予玄をお勧めしたす。 * ** 宿泊斜蚭の予玄 : ** ゲストハりスやカプセルホテルも、人気のある斜蚭はすぐに満宀になるこずがありたす。こちらも早めの予玄を。 * ** 京郜埡所の予玄 : ** 事前予玄が必芁ですので、忘れずに手続きしおください。 * ** 芳光斜蚭の開通時間 : ** 各斜蚭の開通時間を事前に確認し、効率よく芳光できるように蚈画を立おたしょう。 * ** 移動時間 : ** 垂バスや電車での移動時間を考慮し、無理のないスケゞュヌルを立おたしょう。 * ** 持ち物 : ** 歩きやすい靎、防寒具、雚具などを忘れずに準備したしょう。 このプランで、京郜の魅力を存分に楜しんでください 良い旅になりたすように cost_agent : ありがずうございたす 完璧なプランニングですね。補足事項も非垞に的確で、旅行者が安心しお旅を楜しめるよう、现やかな配慮がされおいたす。 このプランを参考に、楜しい京郜旅行を満喫しおください 䜕か他に質問があれば、お気軜にお尋ねください。 review_agent : ありがずうございたす こちらこそ、玠晎らしいプランを䞀緒に䜜り䞊げるこずができ、倧倉光栄です。䜕かお圹に立おるこずがあれば、い぀でもお声がけください。 良い旅になりたすように 単䞀゚ヌゞェントChatGPTのようなの䜿甚ずマルチ゚ヌゞェントの䜿甚の比范 同じ質問に察するChatGPTの出力 【1日目枅氎寺ず祇園】 枅氎寺400円を参拝。䞉幎坂・二幎坂を散策。昌食はカフェで玄1,200円。午埌は祇園を歩く。倕食はうどん玄900円。 合蚈玄3,000円 【2日目嵐山゚リア】 JRで嵐山ぞ240円。竹林の小埄無料、倩韍寺庭園500円。川蟺でお匁圓900円。垌望があれば猿山600円。倕食は1,000円ほど。 合蚈玄3,500円 【3日目䌏芋皲荷ず錊垂堎】 䌏芋皲荷無料。朝食600円。錊垂堎ぞ210円。食べ歩き1,200円。河原町を散策。倕食1,200円。 合蚈玄3,200円 【総費甚玄1〜1.5䞇円宿泊陀く】 䜙裕で5䞇円以内に収たりたす。 安いバヌゞョン、豪華バヌゞョン、が必芁であればお知らせください。 特城 単䞀゚ヌゞェント(ChatGPT) マルチ゚ヌゞェント タスク 䞀぀のモデルがすべおをこなす 専門家ごずに異なる業務を担圓する ゚ラヌチェック なし レビュヌ担圓者が誀りを発芋する 反埩 ナヌザヌは改善を芁求しなければならない ゚ヌゞェントは自動的に互いを掗緎させる 出力品質 良いが、単調である 構造化された、階局化された、より深い出力 結論 オヌトゞェンはAIの掻甚方法におけるパラダむムシフトです。協調知胜を実珟するこずで、単䞀゚ヌゞェントシステムでは達成できない可胜性を解き攟ちたす。開発者、研究者、技術愛奜家の方々に、オヌトゞェンは力になりたす。 オヌトゲンの真䟡は、モデルが賢くなるこずではなく、ワヌクフロヌが効率化されるこずにある。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の12日目の蚘事です。 結論 フロント゚ンドでは、OrvalでzodではなくFetch Clientを生成するこずを怜蚎しおみおはいかがでしょうか Orvalずは Orval ずは、OpenAPIからTypeScriptのコヌドを生成できるツヌルです。 䟋えば、以䞋のようなコヌドが生成できたす。 Fetch Client React Query Zod Hono このように、Orvalは様々な皮類のコヌドを生成できるずおも䟿利なツヌルです。 しかし、䜿い方によっおは、逆に保守性の䜎䞋を匕き起こす可胜性がありたす。 特に、「フロント゚ンドでzodを生成させおいる」こずが課題ずなるケヌスがありたす。 そこで、本蚘事ではフロント゚ンドでOrvalにzodを生成させるこずに぀いお、いく぀かの芳点から考察しおいきたす。 Fetch Clientずいう遞択肢 フロント゚ンドにおいお、なぜOrvalを䜿甚するのでしょうか それは、APIのレスポンスに型が欲しいからです。 zodを生成させるこずで型を埗るこずができたすが、Fetch Clientを生成させるこずでも同様に実珟でき、より簡朔にできる可胜性がありたす。 zodずFetch Clientの比范 では、zodを生成させた堎合ずFetch Clientを生成させた堎合のコヌドを比范しおみたしょう。 OpenAPI openapi: 3.0.0servers: - url: 'http://petstore.swagger.io/v2'info: description: >- This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. version: 1.0.0 title: OpenAPI Petstore license: name: Apache-2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0.html'tags: - name: pet description: Everything about your Pets - name: store description: Access to Petstore orders - name: user description: Operations about userpaths: /pet: post: tags: - pet summary: Add a new pet to the store description: '' operationId: addPet responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '405': description: Invalid input security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' put: tags: - pet summary: Update an existing pet description: '' operationId: updatePet externalDocs: url: "http://petstore.swagger.io/v2/doc/updatePet" description: "API documentation for the updatePet operation" responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found '405': description: Validation exception security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: $ref: '#/components/requestBodies/Pet' /pet/findByStatus: get: tags: - pet summary: Finds Pets by status description: Multiple status values can be provided with comma separated strings operationId: findPetsByStatus parameters: - name: status in: query description: Status values that need to be considered for filter required: true style: form explode: false deprecated: true schema: type: array items: type: string enum: - available - pending - sold default: available responses: '200': description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' '400': description: Invalid status value security: - petstore_auth: - 'read:pets' /pet/findByTags: get: tags: - pet summary: Finds Pets by tags description: >- Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId: findPetsByTags parameters: - name: tags in: query description: Tags to filter by required: true style: form explode: false schema: type: array items: type: string responses: '200': description: successful operation content: application/xml: schema: type: array items: $ref: '#/components/schemas/Pet' application/json: schema: type: array items: $ref: '#/components/schemas/Pet' '400': description: Invalid tag value security: - petstore_auth: - 'read:pets' deprecated: true '/pet/{petId}': get: tags: - pet summary: Find pet by ID description: Returns a single pet operationId: getPetById parameters: - name: petId in: path description: ID of pet to return required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Pet' application/json: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid ID supplied '404': description: Pet not found security: - api_key: [] post: tags: - pet summary: Updates a pet in the store with form data description: '' operationId: updatePetWithForm parameters: - name: petId in: path description: ID of pet that needs to be updated required: true schema: type: integer format: int64 responses: '405': description: Invalid input security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: content: application/x-www-form-urlencoded: schema: type: object properties: name: description: Updated name of the pet type: string status: description: Updated status of the pet type: string delete: tags: - pet summary: Deletes a pet description: '' operationId: deletePet parameters: - name: api_key in: header required: false schema: type: string - name: petId in: path description: Pet id to delete required: true schema: type: integer format: int64 responses: '400': description: Invalid pet value security: - petstore_auth: - 'write:pets' - 'read:pets' '/pet/{petId}/uploadImage': post: tags: - pet summary: uploads an image description: '' operationId: uploadFile parameters: - name: petId in: path description: ID of pet to update required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/ApiResponse' security: - petstore_auth: - 'write:pets' - 'read:pets' requestBody: content: multipart/form-data: schema: type: object properties: additionalMetadata: description: Additional data to pass to server type: string file: description: file to upload type: string format: binary /store/inventory: get: tags: - store summary: Returns pet inventories by status description: Returns a map of status codes to quantities operationId: getInventory responses: '200': description: successful operation content: application/json: schema: type: object additionalProperties: type: integer format: int32 security: - api_key: [] /store/order: post: tags: - store summary: Place an order for a pet description: '' operationId: placeOrder responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid Order requestBody: content: application/json: schema: $ref: '#/components/schemas/Order' description: order placed for purchasing the pet required: true '/store/order/{orderId}': get: tags: - store summary: Find purchase order by ID description: >- For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions operationId: getOrderById parameters: - name: orderId in: path description: ID of pet that needs to be fetched required: true schema: type: integer format: int64 minimum: 1 maximum: 5 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/Order' application/json: schema: $ref: '#/components/schemas/Order' '400': description: Invalid ID supplied '404': description: Order not found delete: tags: - store summary: Delete purchase order by ID description: >- For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId: deleteOrder parameters: - name: orderId in: path description: ID of the order that needs to be deleted required: true schema: type: string responses: '400': description: Invalid ID supplied '404': description: Order not found /user: post: tags: - user summary: Create user description: This can only be done by the logged in user. operationId: createUser responses: default: description: successful operation security: - api_key: [] requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Created user object required: true /user/createWithArray: post: tags: - user summary: Creates list of users with given input array description: '' operationId: createUsersWithArrayInput responses: default: description: successful operation security: - api_key: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/createWithList: post: tags: - user summary: Creates list of users with given input array description: '' operationId: createUsersWithListInput responses: default: description: successful operation security: - api_key: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/login: get: tags: - user summary: Logs user into the system description: '' operationId: loginUser parameters: - name: username in: query description: The user name for login required: true schema: type: string pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' - name: password in: query description: The password for login in clear text required: true schema: type: string responses: '200': description: successful operation headers: Set-Cookie: description: >- Cookie authentication key for use with the `api_key` apiKey authentication. schema: type: string example: AUTH_KEY=abcde12345; Path=/; HttpOnly X-Rate-Limit: description: calls per hour allowed by the user schema: type: integer format: int32 X-Expires-After: description: date in UTC when token expires schema: type: string format: date-time content: application/xml: schema: type: string application/json: schema: type: string '400': description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session description: '' operationId: logoutUser responses: default: description: successful operation security: - api_key: [] '/user/{username}': get: tags: - user summary: Get user by user name description: '' operationId: getUserByName parameters: - name: username in: path description: The name that needs to be fetched. Use user1 for testing. required: true schema: type: string responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/User' application/json: schema: $ref: '#/components/schemas/User' '400': description: Invalid username supplied '404': description: User not found put: tags: - user summary: Updated user description: This can only be done by the logged in user. operationId: updateUser parameters: - name: username in: path description: name that need to be deleted required: true schema: type: string responses: '400': description: Invalid user supplied '404': description: User not found security: - api_key: [] requestBody: content: application/json: schema: $ref: '#/components/schemas/User' description: Updated user object required: true delete: tags: - user summary: Delete user description: This can only be done by the logged in user. operationId: deleteUser parameters: - name: username in: path description: The name that needs to be deleted required: true schema: type: string responses: '400': description: Invalid username supplied '404': description: User not found security: - api_key: []externalDocs: description: Find out more about Swagger url: 'http://swagger.io'components: requestBodies: UserArray: content: application/json: schema: type: array items: $ref: '#/components/schemas/User' description: List of user object required: true Pet: content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' description: Pet object that needs to be added to the store required: true securitySchemes: petstore_auth: type: oauth2 flows: implicit: authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' scopes: 'write:pets': modify pets in your account 'read:pets': read your pets api_key: type: apiKey name: api_key in: header schemas: Order: title: Pet Order description: An order for a pets from the pet store type: object properties: id: type: integer format: int64 petId: type: integer format: int64 quantity: type: integer format: int32 shipDate: type: string format: date-time status: type: string description: Order Status enum: - placed - approved - delivered complete: type: boolean default: false xml: name: Order Category: title: Pet category description: A category for a pet type: object properties: id: type: integer format: int64 name: type: string pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' xml: name: Category User: title: a User description: A User who is purchasing from the pet store type: object properties: id: type: integer format: int64 username: type: string firstName: type: string lastName: type: string email: type: string password: type: string phone: type: string userStatus: type: integer format: int32 description: User Status xml: name: User Tag: title: Pet Tag description: A tag for a pet type: object properties: id: type: integer format: int64 name: type: string xml: name: Tag Pet: title: a Pet description: A pet for sale in the pet store type: object required: - name - photoUrls properties: id: type: integer format: int64 category: $ref: '#/components/schemas/Category' name: type: string example: doggie photoUrls: type: array xml: name: photoUrl wrapped: true items: type: string tags: type: array xml: name: tag wrapped: true items: $ref: '#/components/schemas/Tag' status: type: string description: pet status in the store deprecated: true enum: - available - pending - sold xml: name: Pet ApiResponse: title: An uploaded response description: Describes the result of uploading an image resource type: object properties: code: type: integer format: int32 type: type: string message: type: string openapi : 3.0.0 servers : - url : ' http://petstore.swagger.io/v2 ' info : description : >- This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. version : 1.0.0 title : OpenAPI Petstore license : name : Apache-2.0 url : ' https://www.apache.org/licenses/LICENSE-2.0.html ' tags : - name : pet description : Everything about your Pets - name : store description : Access to Petstore orders - name : user description : Operations about user paths : /pet : post : tags : - pet summary : Add a new pet to the store description : '' operationId : addPet responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 405 ' : description : Invalid input security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : $ref : ' #/components/requestBodies/Pet ' put : tags : - pet summary : Update an existing pet description : '' operationId : updatePet externalDocs : url : " http://petstore.swagger.io/v2/doc/updatePet " description : " API documentation for the updatePet operation " responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Pet not found ' 405 ' : description : Validation exception security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : $ref : ' #/components/requestBodies/Pet ' /pet/findByStatus : get : tags : - pet summary : Finds Pets by status description : Multiple status values can be provided with comma separated strings operationId : findPetsByStatus parameters : - name : status in : query description : Status values that need to be considered for filter required : true style : form explode : false deprecated : true schema : type : array items : type : string enum : - available - pending - sold default : available responses : ' 200 ' : description : successful operation content : application/xml : schema : type : array items : $ref : ' #/components/schemas/Pet ' application/json : schema : type : array items : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid status value security : - petstore_auth : - ' read:pets ' /pet/findByTags : get : tags : - pet summary : Finds Pets by tags description : >- Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. operationId : findPetsByTags parameters : - name : tags in : query description : Tags to filter by required : true style : form explode : false schema : type : array items : type : string responses : ' 200 ' : description : successful operation content : application/xml : schema : type : array items : $ref : ' #/components/schemas/Pet ' application/json : schema : type : array items : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid tag value security : - petstore_auth : - ' read:pets ' deprecated : true ' /pet/{petId} ' : get : tags : - pet summary : Find pet by ID description : Returns a single pet operationId : getPetById parameters : - name : petId in : path description : ID of pet to return required : true schema : type : integer format : int64 responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Pet ' application/json : schema : $ref : ' #/components/schemas/Pet ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Pet not found security : - api_key : [] post : tags : - pet summary : Updates a pet in the store with form data description : '' operationId : updatePetWithForm parameters : - name : petId in : path description : ID of pet that needs to be updated required : true schema : type : integer format : int64 responses : ' 405 ' : description : Invalid input security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : content : application/x-www-form-urlencoded : schema : type : object properties : name : description : Updated name of the pet type : string status : description : Updated status of the pet type : string delete : tags : - pet summary : Deletes a pet description : '' operationId : deletePet parameters : - name : api_key in : header required : false schema : type : string - name : petId in : path description : Pet id to delete required : true schema : type : integer format : int64 responses : ' 400 ' : description : Invalid pet value security : - petstore_auth : - ' write:pets ' - ' read:pets ' ' /pet/{petId}/uploadImage ' : post : tags : - pet summary : uploads an image description : '' operationId : uploadFile parameters : - name : petId in : path description : ID of pet to update required : true schema : type : integer format : int64 responses : ' 200 ' : description : successful operation content : application/json : schema : $ref : ' #/components/schemas/ApiResponse ' security : - petstore_auth : - ' write:pets ' - ' read:pets ' requestBody : content : multipart/form-data : schema : type : object properties : additionalMetadata : description : Additional data to pass to server type : string file : description : file to upload type : string format : binary /store/inventory : get : tags : - store summary : Returns pet inventories by status description : Returns a map of status codes to quantities operationId : getInventory responses : ' 200 ' : description : successful operation content : application/json : schema : type : object additionalProperties : type : integer format : int32 security : - api_key : [] /store/order : post : tags : - store summary : Place an order for a pet description : '' operationId : placeOrder responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Order ' application/json : schema : $ref : ' #/components/schemas/Order ' ' 400 ' : description : Invalid Order requestBody : content : application/json : schema : $ref : ' #/components/schemas/Order ' description : order placed for purchasing the pet required : true ' /store/order/{orderId} ' : get : tags : - store summary : Find purchase order by ID description : >- For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions operationId : getOrderById parameters : - name : orderId in : path description : ID of pet that needs to be fetched required : true schema : type : integer format : int64 minimum : 1 maximum : 5 responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/Order ' application/json : schema : $ref : ' #/components/schemas/Order ' ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Order not found delete : tags : - store summary : Delete purchase order by ID description : >- For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors operationId : deleteOrder parameters : - name : orderId in : path description : ID of the order that needs to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid ID supplied ' 404 ' : description : Order not found /user : post : tags : - user summary : Create user description : This can only be done by the logged in user. operationId : createUser responses : default : description : successful operation security : - api_key : [] requestBody : content : application/json : schema : $ref : ' #/components/schemas/User ' description : Created user object required : true /user/createWithArray : post : tags : - user summary : Creates list of users with given input array description : '' operationId : createUsersWithArrayInput responses : default : description : successful operation security : - api_key : [] requestBody : $ref : ' #/components/requestBodies/UserArray ' /user/createWithList : post : tags : - user summary : Creates list of users with given input array description : '' operationId : createUsersWithListInput responses : default : description : successful operation security : - api_key : [] requestBody : $ref : ' #/components/requestBodies/UserArray ' /user/login : get : tags : - user summary : Logs user into the system description : '' operationId : loginUser parameters : - name : username in : query description : The user name for login required : true schema : type : string pattern : ' ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ ' - name : password in : query description : The password for login in clear text required : true schema : type : string responses : ' 200 ' : description : successful operation headers : Set-Cookie : description : >- Cookie authentication key for use with the `api_key` apiKey authentication. schema : type : string example : AUTH_KEY=abcde12345; Path=/; HttpOnly X-Rate-Limit : description : calls per hour allowed by the user schema : type : integer format : int32 X-Expires-After : description : date in UTC when token expires schema : type : string format : date-time content : application/xml : schema : type : string application/json : schema : type : string ' 400 ' : description : Invalid username/password supplied /user/logout : get : tags : - user summary : Logs out current logged in user session description : '' operationId : logoutUser responses : default : description : successful operation security : - api_key : [] ' /user/{username} ' : get : tags : - user summary : Get user by user name description : '' operationId : getUserByName parameters : - name : username in : path description : The name that needs to be fetched. Use user1 for testing. required : true schema : type : string responses : ' 200 ' : description : successful operation content : application/xml : schema : $ref : ' #/components/schemas/User ' application/json : schema : $ref : ' #/components/schemas/User ' ' 400 ' : description : Invalid username supplied ' 404 ' : description : User not found put : tags : - user summary : Updated user description : This can only be done by the logged in user. operationId : updateUser parameters : - name : username in : path description : name that need to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid user supplied ' 404 ' : description : User not found security : - api_key : [] requestBody : content : application/json : schema : $ref : ' #/components/schemas/User ' description : Updated user object required : true delete : tags : - user summary : Delete user description : This can only be done by the logged in user. operationId : deleteUser parameters : - name : username in : path description : The name that needs to be deleted required : true schema : type : string responses : ' 400 ' : description : Invalid username supplied ' 404 ' : description : User not found security : - api_key : [] externalDocs : description : Find out more about Swagger url : ' http://swagger.io ' components : requestBodies : UserArray : content : application/json : schema : type : array items : $ref : ' #/components/schemas/User ' description : List of user object required : true Pet : content : application/json : schema : $ref : ' #/components/schemas/Pet ' application/xml : schema : $ref : ' #/components/schemas/Pet ' description : Pet object that needs to be added to the store required : true securitySchemes : petstore_auth : type : oauth2 flows : implicit : authorizationUrl : ' http://petstore.swagger.io/api/oauth/dialog ' scopes : ' write:pets ' : modify pets in your account ' read:pets ' : read your pets api_key : type : apiKey name : api_key in : header schemas : Order : title : Pet Order description : An order for a pets from the pet store type : object properties : id : type : integer format : int64 petId : type : integer format : int64 quantity : type : integer format : int32 shipDate : type : string format : date-time status : type : string description : Order Status enum : - placed - approved - delivered complete : type : boolean default : false xml : name : Order Category : title : Pet category description : A category for a pet type : object properties : id : type : integer format : int64 name : type : string pattern : ' ^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$ ' xml : name : Category User : title : a User description : A User who is purchasing from the pet store type : object properties : id : type : integer format : int64 username : type : string firstName : type : string lastName : type : string email : type : string password : type : string phone : type : string userStatus : type : integer format : int32 description : User Status xml : name : User Tag : title : Pet Tag description : A tag for a pet type : object properties : id : type : integer format : int64 name : type : string xml : name : Tag Pet : title : a Pet description : A pet for sale in the pet store type : object required : - name - photoUrls properties : id : type : integer format : int64 category : $ref : ' #/components/schemas/Category ' name : type : string example : doggie photoUrls : type : array xml : name : photoUrl wrapped : true items : type : string tags : type : array xml : name : tag wrapped : true items : $ref : ' #/components/schemas/Tag ' status : type : string description : pet status in the store deprecated : true enum : - available - pending - sold xml : name : Pet ApiResponse : title : An uploaded response description : Describes the result of uploading an image resource type : object properties : code : type : integer format : int32 type : type : string message : type : string orval.config.ts /** * @summary Add a new pet to the store */export const addPetBodyCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetBody = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetBodyCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store');export const addPetResponseCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetResponse = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetResponseCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store'); /** * @ summary Add a new pet to the store */ export const addPetBodyCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetBody = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetBodyCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; export const addPetResponseCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetResponse = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetResponseCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; zodの堎合: /** * @summary Add a new pet to the store */export const addPetBodyCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetBody = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetBodyCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store');export const addPetResponseCategoryNameRegExp = new RegExp( '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$',);export const addPetResponse = zod .object({ id: zod.number().optional(), category: zod .object({ id: zod.number().optional(), name: zod.string().regex(addPetResponseCategoryNameRegExp).optional(), }) .optional() .describe('A category for a pet'), name: zod.string(), photoUrls: zod.array(zod.string()), tags: zod .array( zod .object({ id: zod.number().optional(), name: zod.string().optional(), }) .describe('A tag for a pet'), ) .optional(), status: zod .enum(['available', 'pending', 'sold']) .optional() .describe('pet status in the store'), }) .describe('A pet for sale in the pet store'); /** * @ summary Add a new pet to the store */ export const addPetBodyCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetBody = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetBodyCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; export const addPetResponseCategoryNameRegExp = new RegExp ( ' ^[a-zA-Z0-9]+[a-zA-Z0-9 \\ . \\ -_]*[a-zA-Z0-9]+$ ' , ) ; export const addPetResponse = zod . object ( { id : zod . number () . optional () , category : zod . object ( { id : zod . number () . optional () , name : zod . string () . regex ( addPetResponseCategoryNameRegExp ) . optional () , } ) . optional () . describe ( ' A category for a pet ' ) , name : zod . string () , photoUrls : zod . array ( zod . string ()) , tags : zod . array ( zod . object ( { id : zod . number () . optional () , name : zod . string () . optional () , } ) . describe ( ' A tag for a pet ' ) , ) . optional () , status : zod . enum ([ ' available ' , ' pending ' , ' sold ' ]) . optional () . describe ( ' pet status in the store ' ) , } ) . describe ( ' A pet for sale in the pet store ' ) ; Fetch Clientの堎合: /** * @summary Add a new pet to the store */export type addPetResponse200 = { data: Pet; status: 200;};export type addPetResponse405 = { data: null; status: 405;};export type addPetResponseComposite = addPetResponse200 | addPetResponse405;export type addPetResponse = addPetResponseComposite & { headers: Headers;};export const getAddPetUrl = () => { return `/pet`;};export const addPet = async ( petBody: PetBody, options?: RequestInit,): Promise<addPetResponse> => { const res = await fetch(getAddPetUrl(), { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ...options?.headers }, body: JSON.stringify(petBody), }); const body = [204, 205, 304].includes(res.status) ? null : await res.text(); const data: addPetResponse['data'] = body ? JSON.parse(body) : {}; return { data, status: res.status, headers: res.headers } as addPetResponse;}; /** * @ summary Add a new pet to the store */ export type addPetResponse200 = { data : Pet ; status : 200 ; } ; export type addPetResponse405 = { data : null ; status : 405 ; } ; export type addPetResponseComposite = addPetResponse200 | addPetResponse405 ; export type addPetResponse = addPetResponseComposite & { headers : Headers ; } ; export const getAddPetUrl = () => { return ` /pet ` ; } ; export const addPet = async ( petBody : PetBody , options ?: RequestInit , ) : Promise < addPetResponse > => { const res = await fetch ( getAddPetUrl () , { ... options , method : ' POST ' , headers : { ' Content-Type ' : ' application/json ' , ... options ?. headers }, body : JSON . stringify ( petBody ) , } ) ; const body = [ 204 , 205 , 304 ] . includes ( res . status ) ? null : await res . text () ; const data : addPetResponse [ ' data ' ] = body ? JSON . parse ( body ) : {} ; return { data , status : res . status , headers : res . headers } as addPetResponse ; } ; zodを生成させた堎合、生成されるのはあくたでもzodのスキヌマなので、実際にfetchする凊理は自分で曞く必芁がありたす。 それに察しお、Fetch Clientを生成させた堎合はfetchする凊理たで生成しおくれるので、ボむラヌプレヌトを枛らすこずができたす。 型安党性に぀いおの考察 Fetch Clientで生成させたコヌドを芋おもらうずわかりたすが、 as ​を䜿甚しお型のアサヌションを行っおいたす。 そうです。Fetch Clientで生成したコヌドは厳密には型安党ではありたせん。 しかし、ここで考えおみたいこずがありたす。 型安党ではないこずによっお、問題が発生するのはどのようなケヌスでしょうか それは、OpenAPIのスキヌマず実際にバック゚ンドから返っおくるスキヌマが異なるケヌスです。 そしお、それは果たしおフロント゚ンドの、しかもランタむム䞊で怜知すべきこずなのでしょうか それを螏たえるず、OpenAPIずバック゚ンドの霟霬がフロント゚ンドのランタむム䞊で刀明するのは理想的なタむミングずは蚀えないかもしれたせん。 したがっお、この問題はバック゚ンドの責務ず考え、バック゚ンド偎のテストで察凊する方が適切だず考えられたす。 バック゚ンドはフロント゚ンドを信頌しおはいけたせんが、フロント゚ンドはバック゚ンドを信頌するずいう考え方もできたす。 ランタむム怜蚌に぀いおの考え方 実際問題、ランタむム゚ラヌが発生したらどうするのか ずいう懞念もあるかず思いたす。 その堎合、玠盎に゚ラヌをthrowするずいう遞択肢がありたす。 OpenAPIずバック゚ンドに霟霬があるずいう臎呜的な問題が発生しおいる堎合、フロント゚ンド偎でできるこずは限られおいたす。 たた、Next.jsなら error.tsx ​を配眮しおおくこずで、゚ラヌ画面を衚瀺するこずができたす。 catchした埌どうするのか を念頭においお゚ラヌハンドリングを蚭蚈したしょう。 ク゚リパラメヌタやリク゚ストボディのバリデヌションに぀いお フロント゚ンドからバック゚ンドのAPIにリク゚ストを送る前に、ク゚リパラメヌタやリク゚ストボディのバリデヌションを行いたいずいうナヌスケヌスがあるず思いたす。 ここでは、䞀぀の考え方ずしお、その段階でのバリデヌションの必芁性に぀いお怜蚎しおみたす。 実際は、その盎埌にバック゚ンドがバリデヌションを行いたす。 したがっお、フロント゚ンド偎での重耇したバリデヌションは省略できる堎合が倚いです。 たた、フロント゚ンドで行うバリデヌションはUXのためであるずいう芖点を持぀こずが重芁です。 ぀たり、セキュリティや䞍正な倀を防ぐためのバリデヌションはバック゚ンドで行い、フロント゚ンドずバック゚ンドで二床同じバリデヌションを行う必芁性は䜎いず考えるこずができたす。 フォヌムのバリデヌションに぀いお UXのためのフォヌムのバリデヌションで、zodのスキヌマが欲しくなるケヌスがあるかもしれたせん。 その堎合は、フォヌムのスキヌマをAPIのスキヌマずは別に定矩するこずをおすすめしたす。 なぜなら、フォヌムのスキヌマはAPIのスキヌマず必ず察応しおいるずは限らないからです。 䟋えば、郵䟿番号を入力するずき、API偎では半角数字の文字列を期埅したすが、フォヌム偎ではUXのために党角数字の文字列も受け取れるようにしたい堎合がありたす。 たた、数倀入力でも、API偎では数倀型を期埅したすが、フォヌム偎では䞀時的に文字列ずしお扱い、カンマ区切りの衚瀺に察応したい堎合がありたす。 それ以倖でも、API偎は完成圢のオブゞェクトを期埅したすが、フォヌムでは段階的に異なる圢状のデヌタを扱うような堎合がありたす。 このような堎合、API偎のスキヌマは流甚できたせん。フロント゚ンド偎で独自に定矩する必芁がありたす。 こうなるず、APIのスキヌマを流甚するものず、しないものが混圚するこずになりたす。 そしお、珟状流甚できおいるスキヌマも、埌から倉曎される可胜性がありたす。 ぀たり、APIのスキヌマずフォヌムのスキヌマは本質的に異なるものであるず考えるこずができたす。 したがっお、フォヌムのスキヌマは仮にAPIのスキヌマず䞀臎しおいおも、別で定矩するこずを怜蚎しおみおはいかがでしょうか。 zodを生成した方が適しおいるケヌス ここたで、zodを生成しない遞択肢に぀いお考察しおきたしたが、zodを生成した方が適しおいるケヌスもありたす。 倖郚サヌビスのAPIを䜿甚する堎合 自分たちが管理しおいない倖郚APIを䜿甚する堎合は、zodによるランタむム怜蚌が有効な遞択肢ずなりたす。 なぜなら、OpenAPIスキヌマずバック゚ンドの実装に霟霬があっおも、バック゚ンド偎で修正するこずができないためです。 たた、倖郚APIは予告なく仕様が倉曎されるこずもありたす。 このような堎合、フロント゚ンド偎で防埡的にランタむム怜蚌し、䞍正なデヌタを早期に怜出するこずで、予期しない゚ラヌを防ぐこずができたす。 バック゚ンドでTypeScriptを䜿甚しおいる堎合 バック゚ンドでTypeScriptを䜿甚しおいる堎合、Orvalによるzod生成を掻甚するこずで、バック゚ンド偎でバリデヌションを行うこずができたす。 ただし、これはフロント゚ンドの話ではなく、バック゚ンドの話です。 (ここたで「フロント゚ンドにおける」ず匷調しおきたのはこのためです) バック゚ンドでは、フロント゚ンドから送られおくるリク゚ストボディやク゚リパラメヌタを信頌すべきではありたせん。 したがっお、バック゚ンド偎でランタむムバリデヌションを行う必芁がありたす。 このずき、OpenAPIからzodスキヌマを生成するこずで、バリデヌションのコヌドを自動生成でき、保守性を向䞊させるこずができたす。 補足: APIの型の䜿い方に぀いお zodの話ずは少し逞れたすが、Orvalを䜿う際に泚意したい点ずしお、APIの型をコンポヌネントからAPIクラむアントたで䜿い回すずいうアンチパタヌンがありたす。 前提ずしお、APIのレスポンスはJSONであり、それはシリアラむズされたDTOに過ぎたせん。 JSONはドメむン知識を持たず、ドメむンモデルずしお機胜しないため、Orvalが生成した型をフロント゚ンド偎であたかもドメむンモデルであるかのように盎接䟝存するず、様々な箇所で䞍敎合が発生する可胜性がありたす。 そのため、フロント゚ンドではフロント゚ンド甚のドメむンモデルを定矩するこずをおすすめしたす。 そしお、DTOをドメむンモデルに倉換するMapperを実装するこずも、䞀぀の有効なアプロヌチです。 この蟺りの話は以䞋の蚘事が参考になるため、よろしければ読んでみおください。 フロント゚ンド゚ンゞニアが「自分はJSON色付け係」ず自虐する理由を考察した フロント゚ンドにおける「型」の責任分解に察する1぀のアプロヌチ たずめ 本蚘事では、フロント゚ンドでOrvalを䜿甚する際に、zodではなくFetch Clientを生成するずいう遞択肢に぀いお考察したした。 重芁なポむントは以䞋の4぀です。 Fetch Clientを生成するこずで、zodよりも簡朔に型が぀いたAPI通信の凊理を曞ける ランタむム䞊でのOpenAPIずバック゚ンドの霟霬怜出は、バック゚ンドのテストで察凊する考え方もある フォヌムのスキヌマずAPIのスキヌマは本質的に異なる目的を持぀ APIのレスポンスはシリアラむズされたDTOであり、ドメむンモデルずは区別しお扱うこずが望たしい これらは䞀぀の考え方であり、プロゞェクトの状況やチヌムの方針によっお最適な遞択は異なりたす。 それぞれのアプロヌチのトレヌドオフを理解した䞊で、プロゞェクトに適した方法を遞択するこずをおすすめしたす。
本蚘事は【 Advent Calendar 2025 】の11日目の蚘事です。 はじめに こんにちはマむナビのN.Yです。 2025幎12/1(月)から12/5(金)に開催された「re:Invent2025」に参加したした。 自分にずっお初海倖出匵、初アメリカで䞍安もありたしたが、 結果的に倚くの孊びがあり、あっずいう間に過ぎた週間をご玹介したいず思いたす。 筆者に぀いお 新卒入瀟3幎目 瀟内のクラりド支揎郚眲所属 英語ずは倧孊受隓でお別れ、英語圏ぞの枡航経隓れロ re:Inventずは AWSが䞻催する䞖界最倧芏暡のクラりドカンファレンスで、毎幎12月に米囜ネバダ州ラスベガスで開催されたす。 今幎は14回目の開催ずなり、䞖界䞭から玄60,000人、日本からも1900人近くが参加したらしいです。 基調講挔Keynoteをはじめ、3000以䞊のセッション、パヌトナヌ䌁業の展瀺䌚EXPO、たくさんのむベント、ノベルティグッズなど、孊びから゚ンタメたで幅広く展開されたす。 䌚堎 「ラスベガス・ストリップ」ず呌ばれるカゞノ街にある耇数のホテルが䌚堎になりたす。 最も離れおいるホテルは歩いお1時間かかるほどの距離がありたす。 開催期間䞭、参加者は䌚堎間を぀なぐシャトルバスやモノレヌルが無料で利甚できるため、それらを駆䜿しお移動したす。 ホテル自䜓も巚倧なので、スケゞュヌルを組む際には移動時間も考慮する必芁がありたす。 今回、マむナビメンバヌが宿泊した Harrah's は、メむン䌚堎のVenetianぞも歩いお行ける距離にあり、アクセス良奜でずおも䟿利でした。 䞀週間の過ごし方 ここからは開催期間䞭の様子を、写真を亀えながらご玹介したす。 前段で述べたように、期間䞭さたざたなむベントやセッションが開催されおいたす。 あくたでも初めお参加した人間が䜓隓した䞀䟋ずしお眺めおいただければず思いたす。 ざっくりスケゞュヌル 11/30 矜田空枯出発 -- 日付倉曎線 -- 11/30 サンフランシスコ経由でラスベガス到着 12/1  12/4 re:Invent参加 12/5 ラスベガス出発、サンフランシスコ空枯経由 -- 日付倉曎線 -- 12/6 矜田空枯到着 2025/11/300日目 15:00 矜田空枯に集合 指定されおいた集合時間は15:00でしたが、心配性のマむナビメンバヌは13:00くらいには党員集合しおいたした。 今回はJTBツアヌで参加したため、案内に埓っお搭乗刞発行、荷物預け、保安怜査ぞず倧きなトラブルなく進むこずができたした。 18:00(JTC) 矜田空枯→サンフランシスコぞ出発 箄9時間の長距離フラむトです。機内食が2回提䟛されたした。 有料で機内Wi-Fiが利甚でき、過去のレポヌト蚘事を読んだり、セッションスケゞュヌルを組み盎したりした埌は、寝お過ごしたした。 ヌヌ日付倉曎線ヌヌ 19:00(PST) サンフランシスコ→ラスベガス到着 トランゞットのサンフランシスコで玄6時間埅機の埌、 1時間ほど飛行機の出発が遅れ、珟地時間の19時頃にラスベガスハヌリヌダヌド空枯に到着したした。 re:Invent参加者は、はじめに入堎バッゞを取埗する必芁があり、空枯や䌚堎で手続きができるのですが、この日は間に合わず翌日に取埗するこずにしたした。 ホテルに着いた埌は、荷物を眮いお早速ラスベガスの街を軜く散策したした。 翌日に備えお早めにホテルに戻っお就寝したした。 2025/12/0日目 8:00 ホテルベネチアンぞ移動 初めおのラスベガスの朝 先茩ず、すごい、でかい、すごい、ずか蚀いながら本䌚堎の Venetian ぞ移動したした。 入堎バッゞを取埗し、たずは朝食。 果おしなく広い䌚堎に円卓が広がっおおり、ビュッフェ圢匏で朝食が甚意されおいたした。 予玄しおいたセッションたで時間があったため、毎幎恒䟋ずなっおいるDatadog瀟提䟛の滑り台を滑ったり、巚倧黒板に瀟名を刻んだりしたした。 SWAG゚リアに行くず、re:Invent参加者特兞のパヌカヌずボトルをゲットできたした。 䌚堎の至る所に絊氎できるずころがあるので、もらったボトルは持ち歩いおおくず䟿利です。 パヌカヌは裏起毛で、ラスベガスの倜や なぜか冷房が効いおいる䌚堎では非垞に助けられたした。 10:00 Mandalay Bayぞ移動 Venetianから、最南の䌚堎であるMandalay Bayたでシャトルバスに乗っお20分ほどで到着したした。 午埌は、参加者が4名1チヌムに分かれお他チヌムず察戊する圢匏のセッションである「Game Day」に参加したり、いく぀かの実践型セッションを受講したした。 18:00 セッション終了 党おのセッションを終えお、開催前に日本での re:Invent参加者亀流䌚で知り合った方ず合流したした。 開催期間䞭は日本人が集たりがちず噂のお店「noodle asia」に行きたした。 私たち以倖に日本人はおらず、時間垯によるのかなず思い぀぀、 3000円くらいするワンタンメンを、味わいながらいただきたした。 2025/12/2日目 7:00 KeyNote この日は re:Invent メむンコンテンツの぀である AWS CEO Matt Garman氏の Keynote が午前䞭にあるため、早めにホテルを出おVenetian䌚堎ぞ向かいたす。 朝食を食べおから䌚堎ぞ向かったずころ、すでに本䌚堎は満員だったため䞭継䌚堎ぞ案内されたした。 本䌚堎に入るためには1時間前くらいから䞊んでおく必芁がありそうです、、 CEOのKeynoteでは、新しい機胜やサヌビスが次々に発衚されたした。 サヌビスアップデヌト玹介タむムアタックが始たったずきは、聎衆をワクワクさせる挔出に感動したした。 興味のある方はAWS の公匏Youtubeに公開されおいるので芋おみおください。 䌚堎ぞ向かう途䞭、バケットくんに遭遇 気さくな方で、䞀緒に写真を撮っおくれたした。 圓日に Keynote でGAが発衚された、S3 Vectors のゞャケット着甚バヌゞョンでアツいです。 バケットくんに぀いおもっず知りたい方はこちらも参照 【AWS re:Invent2024】S3バケットくん培底解剖 | マむナビ゚ンゞニアブログ Keynoteが終わった埌は Caesars Forum ぞ向かい、AWSに぀いお楜しみながら孊べるカヌドゲヌム「BuilderCards」をゲットしたした。 瀟内でメンバヌを募っお遊がうず思いたす。 11:00 セッション 午埌のセッションのため、Mandalay Bayぞ移動。 「DDos攻撃からAWS WAFを掻甚しおサヌビスを守り品質を保぀」ずいうシナリオのGameDayにマむナビ゚ンゞニア2名で参加したした。 セッション名AWS GameDay - Winning the DDos Game (SEC403-R) GameDayは、参加者が4人1チヌムに分かれお競うセッションです。 結果は17チヌム䞭4䜍でした。チヌムメむトの倖囜人が぀よ぀よだった 高めの蚀語の壁はありたしたが、WAFコン゜ヌル画面での操䜜や、攻撃的なアクセスを特定しお制埡する方法は勉匷になりたした。 終了埌、同じチヌムだったむスラ゚ル人にLinkedInのアカりントを持っおるか聞かれたした。 海倖の方ずの亀流は名刺亀換ではなく、LinkedInのアカりントを䜜成しおおくず良さそうです。 19:00 Japan Night 倜は今回ツアヌに参加させおいただいた JTBさん䞻催の日本人向け亀流むベントに参加したした。 普段、他瀟の゚ンゞニアず亀流する機䌚があたりないのですが、亀流䌚ならではの情報亀換ができお有意矩な時間ずなりたした。 ネットワヌキングも re:Invent の醍醐味であるこずを痛感したした。 2025/12/3日目 1日を通しおセッションに参加したした。 参加したセッション Accelerating Incident Resolution: AI-Driven Root Cause Analysis (COP320-R2) AIによる自動むンシデント調査機胜である CloudWatch Investigation を䜿甚しおアラヌト発生時のトラブルシュヌティング䜓隓 Building Serverless applications with Terraform workshop (CNS312-R) Terraformを䜿ったサヌバレスアヌキテクチャの構築䜓隓ワヌクショップ Amazon ECS observability patterns and design decisions (CNS351-R1) ECSのオブザヌバビリティやベストプラクティスな運甚方法に぀いおスピヌカヌず参加者がディスカッションするChalk talk。翻蚳機がないず、ちょっず䜕蚀っおるか分かんない状態でしたが、アメリカならではの熱いディスカッションを味わえたのは良い経隓でした。 Build Streaming Analytics Dashboards in Minutes with Kiro CLI (IND312-R1) Kiro CLIを䜿甚しお自然蚀語でのCDKアプリケヌションの構築を行うハンズオン。デプロむ埅ち時間が長く、スピヌカヌず参加者が生成AIのアンチテヌれに぀いお議論しおいるのを翻蚳機で聞いおいたした。 この日は他に特筆するこずがないので、 午埌になるず䌚堎に珟れるおや぀コヌナヌの写真を茉せたす。 おかげさたで日䞭垯は食事に困りたせんでした。 2025/12/4日目 8:00 朝食、EXPO 我々が参加するのはこの日が最埌でした。 い぀も通り朝食を枈たせた埌は、時間に䜙裕があったのでEXPOブヌスを巡りたした。 自分は参加できなかったのですが、ブヌスを呚りながらスタンプを集めるこずで豪華ノベルティがもらえる催しなどもあったみたいです。 AWSのデヌタセンタヌで管理されおいる物理サヌバヌが展瀺されおいたした。 業務で物理サヌバヌを芋る機䌚がないので新鮮でした。 12:00 セッション 午埌はハンズオン圢匏のセッションであるWorkShopず、AWS Japanが䞻催する日本人向けのセッション「Japan Wrap-up Session」を聎講したした。 日本語でre:Inventを振り返るこずができたす。 16:00 認定者ラりンゞ Venetian の2階にあるAWS認定資栌保持者のみが入堎できる認定者ラりンゞに寄っおみたした。 事前に認定資栌アカりントの登録をするこずで、最初からバッゞに印チェックマヌクが぀きたす。 自分は登録を忘れおいたので、受付で説明しお Credly の認定蚌画面を芋せるず快く印シヌルを貌っおくれたした。 䞭に入るず軜食や、いく぀かの怅子ずテヌブルが甚意されおいお、倚くの人がく぀ろぎながらKeynoteの䞭継を芳おいたした。 20:00 re:Play re:Inventを締めくくる、倧芏暡屋倖パヌティです。 PM19:00からAM0:00たで開かれおいお、東京ドヌムくらいの広さの䌚堎に飲食ブヌスや䜓隓型むベントブヌス、ラむブ䌚堎が䜵蚭されおいたした。 2025/12/5垰囜 珟地時間の AM3:30 頃にホテルのロビヌに集合し、ラスベガス空枯ぞ。 行きず同じくサンフランシスコ経由で垰りたす。 垰りの飛行機は11時間くらいのフラむトでした。 週間の疲劎ず緊匵がほどけ、着垭した瞬間からの蚘憶がありたせん。 日本時間の12月6日18:00頃、無事に矜田空枯に到着したした。 さようならラスベガス、ただいた日本。 最埌に 今回参加しおみお、䌚堎、むベント、AWSが創造する䞖界のスケヌルの倧きさに圧倒され぀぀、䞖界䞭から参加しおいる゚ンゞニアやAWS゚キスパヌトずの亀流を通しお、普段の業務では埗られない刺激を受けたした。 ゚ンゞニアはもちろん、非゚ンゞニアの方や勉匷䞭の方など、あらゆる立堎の方にずっお参加する䟡倀があるず思いたす。 ずは蚀え、費甚や枡航準備、䜓力などを考えるず気軜に参加できないのも事実です。 参加する機䌚を䞎えおくれた䌚瀟、そしお䞀緒に参加したメンバヌにはずおも感謝しおいたす 最埌たでお読みいただき、ありがずうございたした むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の10日目の蚘事です。 はじめに ITD2-2-1のH・Tです。マむナビで内補開発しおいたす。 開発珟堎で䜜業効率が萜ちるタスクがあったので、自分でラむブラリを䜜ったのでその玹介をしたす。 今埌の内補開発でも倧いに圹立぀かなず思っおたす。 なにを぀くったか Tatsumaki - Rails to TypeSpec Generator npmにすでに公開しおいたす。 https://www.npmjs.com/package/@tyranno269/tatsumaki ラむブラリが提䟛する䟡倀 RailsのDBスキヌマからTypeSpecモデルを自動生成するnpmラむブラリでスキヌマ駆動開発を簡易化できたす。 開発モチベヌション マむナビの開発珟堎では、Rails API + Next.js + zodによる型安党な開発が倚いです、ただ以䞋の課題がありたした。 手動倉換の煩雑さ: Rails schema.rb → TypeSpec → OpenAPI → zodの倉換チェヌンが手動 型同期の遅延: Rails偎の倉曎をフロント゚ンドに反映するのに時間がかかる 型䞍敎合のリスク: 手動倉換によるランタむム゚ラヌの発生 OpenAPIからZod生成では Orval ずいうラむブラリを䜿甚しおいたす。 これによっおOpenAPIからシヌムレスに実装を進められたす。䞀方でバック゚ンドのRailsからTypeSpecに曞き出すのは少々手間をかけおいたした。ここが自分は煩雑だなず感じおたした。これを解決しようず思ったのがラむブラリ開発のモチベヌションです。 Tatsumakiによる解決 # docsディレクトリで実行 npx @tyranno269/tatsumaki # → rails.tsp自動生成 → TypeSpec → OpenAPI → orval → zod型定矩 機胜 Rails schema.rbから自動でTypeSpec生成 Rails enum完党察応 - モデルファむルからenum定矩を自動抜出・生成 monorepo察応で柔軟なプロゞェクト構造をサポヌト 型安党性をRailsからフロント゚ンドたで䞀貫しお確保 Rails互換の単数圢化 ( company_branches ​ → CompanyBranch ​) 参考: activesupport/lib/active_support/inflections.rb 想定プロゞェクト構造 project/ ├── backend/     # Rails API │   ├── app/models/  # Rails enum定矩 │   └── db/ │       └── schema.rb ├── docs/        # TypeSpec (実行堎所) │   └── rails.tsp (生成される) └── frontend/    # Next.js + zod Rails Enum RailsにはModel局にEnum倀を定矩したす。曞き方が倚様にありたす。Schema.rbではintegerですが、APIレスポンスずしお返す倀はStringのケヌスが倚くあるので察応が必芁でした。ただしEnumもi18nで翻蚳ファむルを定矩するずバック゚ンドから日本語化しお返せたすが、フロント゚ンドメンバヌず盞談した結果、翻蚳はFE偎で察応するず良いずの結論になり、玔粋にmodelファむルの定矩をTypeSpecに出力したす。 察応するenum圢匏 class Company < ApplicationRecord # ハッシュ圢匏 enum :company_status, { disabled: 0, enabled: 1, suspended: 9 } # 配列圢匏 enum :status, [ :active, :archived ] # %i蚘法 enum :priority, %i(low medium high) # キヌワヌド匕数 enum priority: { low: 0, medium: 1, high: 2 } end class Book < ApplicationRecord   enum :status, [ :draft, :published, :archived ] end 生成されるTypeSpec namespace CompanyEnums {   enum CompanyStatus {     disabled,     enabled,     suspended,   }   enum Status {     active,     archived,   } } namespace BookEnums {   enum Status {     draft,     published,     archived,   } } model Company {   id: int64;   company_status: CompanyEnums.CompanyStatus; // default: "enabled"   status: CompanyEnums.Status; // default: "active"   created_at: utcDateTime;   updated_at: utcDateTime; } model Book {   id: int64;   name: string;   status: BookEnums.Status; // No naming conflict with CompanyEnums.Status   created_at: utcDateTime;   updated_at: utcDateTime; } enum機胜の特城 名前空間による衝突回避 - CompanyEnums.Status ​ vs BookEnums.Status ​ schema.rbずの連携 - テヌブル定矩があるモデルのみ凊理 型眮換 - int32 ​フィヌルドを適切な enum​ 型に倉換 å…šRails enum構文察応 - ハッシュ、配列、%i蚘法、キヌワヌド匕数 その他にも、 a_matsuda さんの Stateful_enum ずいったようにenum定矩からブロックでむベントを蚘述するずいったGemもありたす。TypeSpecの出力ではブロック郚分は回避する工倫もしたした。 開発フロヌ統合 Tatsumakiの蚭蚈により、Rails schema.rb + enum → TypeSpec → OpenAPI → zod の完党自動化チェヌンを実珟し、型安党なフルスタック開発を支揎可胜になりたした。 むンストヌル・䜿甚方法 # 実行最新版が自動取埗される npx @tyranno269/tatsumaki # 既存ファむル䞊曞き npx @tyranno269/tatsumaki --force # カスタム出力ファむル npx @tyranno269/tatsumaki --out models.tsp --force # 既存ファむルに远蚘 npx @tyranno269/tatsumaki --append マむナビでの今埌の掻甚方法 TypeSpecディレクトリ構成次第にはなりたすが様々なパタヌンの開発で応甚できうるかずおもいたす。 パタヌン1 project/ ├── backend/     # Rails API │   ├── app/models/  # Rails enum定矩 │   └── db/ │       └── schema.rb ├── docs/        # TypeSpec (実行堎所) │   └── rails.tsp (生成される) └── frontend/    # Next.js + zod 生成されるrails.tspにroutesになるオペレヌション情報を蚘述しおいくパタヌン パタヌン2 project/ ├── backend/     # Rails API │   ├── app/models/  # Rails enum定矩 │   └── db/ │       └── schema.rb ├── docs/        # TypeSpec (実行堎所) │   └── rails.tsp (生成される) │   └── routes │       └── admin │           └── admin.tsp (ex:管理サむトの管理者䞀芧・詳现など) │       └── user │           └── notice.tsp (ex:ナヌザヌサむトのお知らせ䞀芧・詳现など) └── frontend/    # Next.js + zod 生成されるrails.tspにroutesで定矩したオペレヌションをimportしお組み蟌んでいくパタヌン パタヌン3 project/ ├── backend/     # Rails API │   ├── app/models/  # Rails enum定矩 │   └── db/ │       └── schema.rb ├── docs/        # TypeSpec (実行堎所) │   └── rails.tsp (生成される) │   └── main.tsp (出力の芪ずなるファむル) │   └── models │       └── admin.tsp (rail.tspからnamespaceの該圓郚分をコピペで定矩) │       └── notice.tsp (rail.tspからnamespaceの該圓郚分をコピペで定矩) │   └── routes │       └── admin │           └── admin.tsp (ex:管理サむトの管理者䞀芧・詳现など) │       └── user │           └── notice.tsp (ex:ナヌザヌサむトのお知らせ䞀芧・詳现など) └── frontend/    # Next.js + zod 生成されるrails.tspからコピペで抜出し利甚しおいくパタヌン 最埌に 実際のRailsのプロゞェクトで䜿甚される䞻芁機胜をカバヌできたかなず思っおいたす。よかったら利甚しおみおください。今回は自分の䜜業時間を枛らしたいモチベヌションで生み出したラむブラリなので実際のナヌスケヌスで察応できおいないこずもあるかなず思いたす。芁望があれば Github Issue に䞊げお頂けたすず嬉しいです むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 】の9日目の蚘事です。 AI戊略宀のM・Wです。この蚘事を曞いおいるのは12月1日です。 11月30日にゞャパンカップで カランダガン の単勝ず䞉連単を取りたした。珟地で芳戊をしおいたのですが、倧倉興奮したレヌスでした。ゎヌル間際の2頭の叩き合いに混ざる空銬。 ぜひYoutubeで動画を芋おください。以䞊、12月11日の蚘事でした 沌の淵 突然ですが、forkやspawnの違いに぀いお知っおいたすでしょうか。 私は知らなかったです。いたも正盎わかりたせん。 その結果、沌にハマっおしたいたした。抜け出せないたた今日を過ごしおいたす。 これは自分甚のメモ+同じようなこずを実装する人がいた際の䞀助になればいいなず思っお執筆しおいたす。 たずずあるAgentを考えたす。このAgentは「環境」を知芚し「行動」を決定するAgentです。 Agentは「行動」を行うず「環境」から「報酬」を手にするこずができ、「環境」は次の「状態」に遷移したす。 図にするずこんな感じです。これは基本的な匷化孊習を説明する際に甚いられる簡略図です。 今回、ボン◯ヌマンのようなPvPをするゲヌムに぀いお、匷化孊習を䜿っお゚ヌゞェントを䜜る機䌚がありたした。぀たり䞊の画像に照らし合わせるず䞋蚘の衚ずなりたす。 甚語 察応 環境 ゲヌム自䜓 ゚ヌゞェント 操䜜キャラクタヌ 状態 その時時の盀面 行動 移動+ボム配眮+キック 報酬 勝利/敗北など レギュレヌションずしお自陣営は2぀のBotを甚意する2vs2の察決ず提瀺されたため、䞊蚘の゚ヌゞェントが2぀必芁なマルチ゚ヌゞェント孊習を行う必芁がありたした。 ちなみに特に実装は指定されなかったのでロゞックで蚘述するでもOKです。AI郚眲なので匷化孊習で挑みたした 沌にダむブ ここでいよいよ掲題に出おきたマルチプロセス孊習の話ができおたす。 最新の匷化孊習アルゎリズムに疎かったので、䞀旊Actor-CriticベヌスのA3Cアルゎリズムを遞択したした。実装したモデル構造は䞋蚘ずなりたす。 盀面の゚ンコヌドやモデル蚭蚈に぀いおは以䞋のモデルを参考ずしおいたす。Cursor先生ありがずう Multi-Agent Training for Pommerman: Curriculum Learning and Population-based Self-Play Approach: https://arxiv.org/abs/2407.00662 マルチプロセス孊習の党䜓感ずしおは䞋蚘の図です。 ゲヌム盀面を提䟛するホスト偎ず゚ヌゞェント間はWebSocketによる疎通を行う必芁があるため、各BotごずにWebSocketサヌバを子プロセスずしお䜜成したす。 ぀たり子プロセス䞊では、ゲヌム開始から終了たで実行されたすが、そこで収集したデヌタに぀いおは䜕かしらの方法を䜿っお芪プロセス䞊に移動させないず、子プロセスがterminateされた際に砎棄されおしたう問題がありたす。 そのため 共有メモリ を䜿っお誀差募配を芪プロセス偎ず共有を行い、最適化噚を䜿っお共有モデルのパラメヌタを曎新、次の孊習ルヌプで子プロセスぞ配分を繰り返し孊習を行っおいきたす。 沌の底 孊習モデルを共有メモリに配眮するためには model.share_memory() を実斜したすが、今回の実装のケヌスにおいおたびたび子プロセスの model.backward() が倱敗する事象がありたした。 いく぀か問題はありたしたが、 ・䞋蚘のログが出力され、backward凊理が倱敗するケヌス objc[8623]: +[MPSGraphObject initialize] may have been in progress in another thread when fork() was called.objc[8623]: +[MPSGraphObject initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug. ・backward()が呌び出された際に子プロセスがクラッシュしお音もなく消える 䜿甚しおる端末がMacOSであるため、前者のケヌスでは $export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES を実行するこずで゚ラヌ文が出力されなくなりたす。しかしながらこれはワヌクアラりンドずなりたす。 MacOSでは fork() が実行された際に、芪プロセスで既に初期化され぀぀あったObjective-Cのデヌタ構造が子プロセスに匕き継がれるず、子プロセス偎でその初期化が安党に完了できない状況が発生するこずがありたす。これにより、子プロセスがクラッシュ通垞はEXC_BAD_ACCESSしたり、デッドロックしたりしたす。 Pythonのmultiprocessingは、デフォルトでは「fork」方匏を䜿っお新しいプロセスを生成するため、この問題に盎面しやすいです。そのため2点目の音もなく子プロセスが消えた原因も含めお、backward()凊理がうたく動䜜しないのは fork() による子プロセスの生成に問題があるず結論付けたした。 ちなみにPyTorchからマルチプロセス孊習する際のベストプラクティスでも「Use an alternative process start methods, such as spawn or forkserver, which ensures a clean initialization of each process.」ず蚘茉がありたす。 https://docs.pytorch.org/docs/stable/notes/multiprocessing.html#poison-fork-in-multiprocessing spawn は fork ずは違い、Pythonむンタヌプリタごずプロセスを生成したす。起動が遅いずいうデメリットはありたすが、必芁な情報のみを芪プロセスから匕き継ぐ点や䞊蚘の初期化に起因するような゚ラヌを回避できるメリットがありたす。 実隓 簡単なNNを構築しおspawnを䜿甚したbackward()がうたくいくかどうかを実隓しおみたす。 import torchimport torch.nn as nnimport torch.multiprocessing as mpimport torch.nn.functional as Fimport torch.optim as optimclass ExampleNN(nn.Module): def __init__(self): super(ExampleNN, self).__init__() self.fc = nn.Linear(10, 1) def forward(self, x): out = self.fc(x) return F.sigmoid(out)def train(xs, target): model = ExampleNN() optimizer = optim.SGD(model.parameters()) out = model(xs) loss = F.binary_cross_entropy(out, target) optimizer.zero_grad() loss.backward() optimizer.step() print('trainメ゜ッドが終了したよ') 䞊蚘のtrainメ゜ッドをforkで動かしおみたす。 if __name__ == '__main__': x = torch.rand(1, 5, 10) t = torch.Tensor([0, 1, 1, 0, 1]).reshape(1, 5, 1) train(x, t) print('-'*54) ctx = mp.get_context('fork') processes = [] for i in range(5): p = ctx.Process(target=train, args=(x, t)) p.start() processes.append(p) for p in processes: p.join()>>> trainメ゜ッドが終了したよ------------------------------------------------------RuntimeError: Unable to handle autograd's threading in combination with fork-based multiprocessing. See https://github.com/pytorch/pytorch/wiki/Autograd-and-Fork 無事゚ラヌが出たした。 ちなみに途䞭の train(x, t) をコメントアりトするず正垞に動䜜したす。これは䞊蚘のGithubにも蚘茉の通り、backward()で呌び出されおいるAutogradがすでに䜿甚されおいる状態でfork()されたこずに起因する゚ラヌずなりたす。 Autograd engine relies on threads pool, which makes it vulnerable to fork. We detect such situations and warn users to use spawn method of multiprocessing. 指瀺通りspawnでやっおみたしょう。 if __name__ == '__main__': x = torch.rand(1, 5, 10) t = torch.Tensor([0, 1, 1, 0, 1]).reshape(1, 5, 1) train(x, t) print('-'*54) ctx = mp.get_context('spawn') processes = [] for i in range(5): p = ctx.Process(target=train, args=(x, t)) p.start() processes.append(p) for p in processes: p.join() >>> trainメ゜ッドが終了したよ------------------------------------------------------trainメ゜ッドが終了したよtrainメ゜ッドが終了したよtrainメ゜ッドが終了したよtrainメ゜ッドが終了したよtrainメ゜ッドが終了したよ 無事゚ラヌなく実行するこずができたした。 この結果から spawn を明瀺的に指定するこずでbackward()凊理の゚ラヌを回避できるこずがわかりたす。 実際の実装は䞋蚘のような圢ずなっおいたす。 ctx = mp.get_context('spawn') # mp.set_start_method('spawn', force=True) # ゚ピ゜ヌドルヌプ for episode in tqdm(range(num_episodes)): clients: List[BombermanClient] = self.create_clients() process_pool = [] for c in clients: p = ctx.Process(target=c.run) p.start() process_pool.append(p) # ゚ピ゜ヌドの孊習 loop = asyncio.get_event_loop() loop.run_until_complete(self.train_episode(max_steps)) print("ロヌルアりト終了") # 子プロセスの終了を埅぀ for p in process_pool: if p.is_alive(): p.terminate() p.join(timeout=3) # 最倧3秒埅぀ if p.is_alive(): # それでも終了しない堎合は匷制終了 print(f"Warning: Process {p.pid} did not terminate gracefully, forcing...") p.kill() p.join() そもそもMacOSだずデフォルトで spawn だそうです。぀たりわざわざ明瀺的に fork を䜿ったずころから党おは始たったのです。Cursorくん、君は頑なにforkを掚しおいたよね・・・。 これで 戊 に臚めたす 終わりに 元々のスタヌト地点ずしお、マむナビグルヌプの䞭の䞀぀である Mynavi Techtus Vietnam さんから挑戊状を叩き぀けられたずころからこの沌は始たりたした。 Techtusさん「ボン◯ヌマンAIを䜜成しおPvPをやろうそれでどこのチヌムが匷いか勝負だ」 結果は・・・ å…šæ•— でした。 そもそもこの問題を解決したのは戊いが終わった3週間埌ぐらいなんです ぀たり、戊には裞同然で挑んだこずになる・・・すでに次の戊は始たっおいるずいう決意を胞に日々沌の䞭で生きたいず思いたす むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の8日目の蚘事です。 ITD 1-2 開発課のO・Aです。最近は流行りの3呚遅れくらいで麻蟣湯にハマっおいるのず、デヌタベヌス呚りに興味がありたす。 今月11月4日に、 囜土亀通省が無償でMCP serverを公開 したこずが話題たぶんになっおいお気になったので、觊っおみたした。 MCPずは 「MCP」Model Context Protocolは、倧芏暡蚀語モデルLLMなどを䜿ったAIアプリケヌションず、倖郚のツヌルやデヌタずの連携を暙準化するプロトコル。2024幎にAnthropicが発衚したもの。 囜土亀通省ずは 日本の囜土の総合的な利甚・開発・保党、瀟䌚資本敎備、亀通政策、気象業務、海䞊の安党確保などを担う䞭倮省庁。 珟圚の囜土亀通倧臣は 金子 恭之 氏 囜土亀通デヌタプラットフォヌムずは https://www.mlit-data.jp 2020幎4月に1.0版ずしお囜土亀通省によっお䞀般公開された。 亀通、河川、枯湟、航空、郜垂開発など、囜土亀通省及び民間の様々なデヌタを、䞀元的に怜玢・可芖化・ダりンロヌドを可胜にするデヌタプラットフォヌム。 略しお 囜亀DPF Webブラりザを通した怜玢・取埗だけでなく、様々 API機胜 の開発・提䟛も行っおいる。 APIリファレンスや開発環境を構築せず簡易にAPIを利甚したい時のための GraphiQL GraphQL䜜成支揎UIツヌル環境の提䟛も行っおいる。 䞻な掻甚 むンフラ維持管理ず蚈画策定 郜垂環境の改善 物流効率化 芳光埩興の掚進 防灜・枛灜 etc
 参考 https://www.mlit.go.jp/tec/tec_tk_000066.html https://www.mlit.go.jp/report/press/content/001362442.pdf 囜土亀通デヌタプラットフォヌム MCPずは 囜土亀通省が保有するデヌタず民間等のデヌタを連携し、䞀元的に怜玢・衚瀺・ダりンロヌドを可胜にする囜土亀通デヌタプラットフォヌムが提䟛する利甚者向けAPIず接続するMCPModel Context Protocolサヌバヌ リポゞトリの抂芁欄より これたではAPI利甚のために、API仕様を調べる -> GraphQLでク゚リを曞く -> APIに投げる必芁がありたしたが、MCPサヌバヌの掻甚で自然蚀語の察話のみでデヌタが取埗可胜になりたした。 準備 リポゞトリ のREADMEに倧䜓わかりやすく蚘茉されおいたす。 䜕が必芁 Claude Desktop Python 3.10以䞊 囜土亀通省DPFのAPIキヌ こちら から取埗。アカりント䜜成必芁あり。 1リポゞトリをcloneしお仮想環境を有効化 git clone https://github.com/MLIT-DATA-PLATFORM/mlit-dpf-mcp.gitcd mlit-dpf-mcppython -m venv .venv.venv\Scripts\activate # Windowssource .venv/bin/activate # macOS/Linux 2䟝存ラむブラリむンストヌル pip install -e .pip install aiohttp pydantic tenacity python-json-logger mcp python-dotenv 3 .env.sample を .env にコピヌ + 環境倉数蚭定 MLIT_API_KEY=`取埗したAPIキヌ`MLIT_BASE_URL=https://www.mlit-data.jp/api/v1/ 4mcpサヌバヌ起動 python -m src.server 5Claude Desktopの蚭定を開いお claude_desktop_config.json に MCP構成を远加 { "mcpServers": { "mlit-dpf-mcp": { "command": "....../mlit-dpf-mcp/.venv/Scripts/python.exe", "args": [ "....../mlit-dpf-mcp/src/server.py" ], "env": { "MLIT_API_KEY": "取埗したAPIキヌ", "MLIT_BASE_URL": "https://www.mlit-data.jp/api/v1/", "PYTHONUNBUFFERED": "1", "LOG_LEVEL": "WARNING" } } }} ※commandずargsの .... の郚分は実際のパスに倉曎する 6保存しおClaude Desktop再起動 詊しおみる 「新宿駅近くの避難所を5件教えお」 Claude Desktop䞊では以䞋のようなリク゚ストずレスポンスがMCPサヌバヌから返っおいるこずがわかりたす リク゚スト { `size`: 5, // 取埗件数䞊限 `term`: `避難所`, // 怜玢キヌワヌド `location_lat`: 35.6896, // 䞭心地点の緯床新宿駅呚蟺 `location_lon`: 139.7006, // 䞭心地点の経床新宿駅呚蟺 `location_distance`: 1000 // 怜玢半埄1km} レスポンス { "search": { "totalNumber": 6, "searchResults": [ { "id": "fb4a919a-09f6-4a3b-b7ca-9fb294f16dc8", "title": "新宿䞭倮公園・高局ビル矀䞀垯", "lat": 35.689666, "lon": 139.689875, "year": "2020", "dataset_id": "nlni_ksj-p20", "catalog_id": "nlni_ksj" }, { "id": "fe039eed-bf70-4638-89d7-283da22739c2", "title": "西新宿䞭孊校", "lat": 35.695942, "lon": 139.694819, "year": "2020", "dataset_id": "nlni_ksj-p20", "catalog_id": "nlni_ksj" }, { "id": "161a9969-7d98-4ea9-b239-2196c8e4ac0e", "title": "鳩森小孊校", "lat": 35.682625, "lon": 139.705994, "year": "2020", "dataset_id": "nlni_ksj-p20", "catalog_id": "nlni_ksj" }, { "id": "feeeaa72-79be-4b02-89ef-ad82e7a64986", "title": "郜立新宿高等孊校", "lat": 35.688478, "lon": 139.705121, "year": "2020", "dataset_id": "nlni_ksj-p20", "catalog_id": "nlni_ksj" }, { "id": "b69d0381-a860-45c7-aad0-87d3f9e076fd", "title": "新宿埡苑", "lat": 35.685518, "lon": 139.709165, "year": "2020", "dataset_id": "nlni_ksj-p20", "catalog_id": "nlni_ksj" } ] }} 実際に䜕が起こっおいるのか ↑geminiに䜜成しおもらった 内郚凊理を芋おみる STEP 1: Claude Desktopがリク゚スト { "method": "tools/call", "params": { "name": "search_by_location_point_distance", "arguments": { "size": 5, "term": "避難所", "location_lat": 35.6896, "location_lon": 139.7006, "location_distance": 1000 } } } STEP 2: handle_call_toolが呌ばれる @server.call_tool() # ← MCPサヌバヌSDKが自動的にルヌティングasync def hasync def handle_call_tool(name: str, arguments: dict) -> List[types.TextContent]: # name = "search_by_location_point_distance" # arguments = {size: 5, term: "避難所", location_lat: 35.6896, ...} rid = new_request_id() # リク゚ストIDを生成 cfg = load_settings() # 蚭定読み蟌み未䜿甚だがロヌド client = MLITClient() # API クラむアント䜜成 STEP3: バリデヌション + Pydanticモデルに倉換 elif name == "search_by_location_point_distance": p = SearchByPoint.model_validate({ "term": arguments.get("term"), "first": arguments.get("first", 0), "size": arguments.get("size", 50), "phrase_match": arguments.get("phrase_match", True), "prefecture_code": arguments.get("prefecture_code"), "point": { "lat": arguments["location_lat"], "lon": arguments["location_lon"], "distance": arguments["location_distance"], } }) STEP4: APIクラむアント呌び出し data = await client.search_by_point( p.point.lat, p.point.lon, p.point.distance, term=p.term or "", first=p.first, size=p.size, phrase_match=p.phrase_match, ) search_by_point メ゜ッドの䞭で、GraphQLを組み立おおいる async def search_by_point(self, lat: float, lon: float, distance_m: float, **kw) -> Dict[str, Any]: loc = self.make_geodistance_filter(lat, lon, distance_m) # make_geodistance_filterで地理オブゞェクトに敎圢 { "geoDistance": { "lat": 35.6896, "lon": 139.7006, "distance": 1000 } } # ク゚リを構築 q = self.build_search(location_filter=loc, **kw) return await self.post_query(q) # ==== (GraphQL): operator 'is' ==== async def search_by_attribute_raw( self, *, term: Optional[str] = None, first: int = 0, size: int = 20, phrase_match: bool = True, attribute_name: str, attribute_value: Any, fields: Optional[str] = None, ) -> Dict[str, Any]: af = self.make_single_attribute_filter(attribute_name, attribute_value) effective_term = term if term is not None else "" q = self.build_search( term=effective_term, first=first, size=size, phrase_match=phrase_match, attribute_filter=af, fields=fields or self._fields_basic(), ) # API送信 return await self.post_query(q) ここで以䞋のGraphQLク゚リが構築される↓ { search( term: "避難所" phraseMatch: true first: 0 size: 5 locationFilter: { geoDistance: { lat: 35.6896 lon: 139.7006 distance: 1000 } } ) { totalNumber searchResults { id title lat lon dataset_id catalog_id } } } STEP5: 最終的にjson圢匏に敎圢しおClaudeDesktopに返华 # 1481行目 text = json.dumps(data, ensure_ascii=False) if len(text.encode("utf-8")) > 1024 * 1024: text = text[:1024 * 512] + "\n...<truncated>" logger.info("tool_done", extra={"rid": rid, "tool": name, "elapsed_ms": t.elapsed_ms}) return [types.TextContent(type="text", text=text)] おわりに 業務での掻甚方法は今の所思い぀かないですが・・・。 こうしたMCPサヌバヌが無償で公開されるおかげで、囜のデヌタ取埗のハヌドルが盞圓䞋がっお䟿利になっおいるこずを実感したした PLATEAUプラトヌ などの3Dモデルデヌタのダりンロヌドもできるようなので、䜕かに掻かしたいですね。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 】の7日目の蚘事です。 組織でのAI掻甚をどう進めるのか。その土台敎備に぀いお調査を進める䞭で、1぀の結論に至りたした。囜内倖の著名な゚ンゞニアや調査レポヌトが䞀貫しお指摘しおいたのは、埓来の゜フトりェア開発の原理原則がこれたで以䞊に重芁になるずいうこずでした。AI時代だから䜕か特別なこずをするのではなく、今たで良いずされおきたこずを、地道に孊習ず実践をしおいくしかないず考えおおりたす。 以䞋では、この考えに至った情報に぀いお、述べおいきたす。 AIは「増幅噚」である 2025幎のDORAレポヌトは、AIの本質を「増幅噚」ず衚珟しおいたす [1] 。高パフォヌマンス組織の匷みを拡倧する䞀方で、苊戊しおいる組織の機胜䞍党も拡倧したす。匷力な自動テスト、成熟したバヌゞョン管理、迅速なフィヌドバックルヌプずいった堅牢な制埡システムがなければ、AIによる倉曎量の増加は技術負債に぀ながる危険性が高くなりたす。 ファむンディ株匏䌚瀟CTO宀の高橋裕之氏は、日本の珟状に譊鐘を鳎らしおいたす [2] 。IT埓事者798名ぞの調査で、開発フレヌムワヌクに぀いお「よくわからない」ず「りォヌタヌフォヌル」が合わせお55%を占めたした。旧匏のバヌゞョン管理ツヌルを䜿甚しおいる割合も高く、GitHub Copilotなどを掻甚できる土台すら敎っおいたせん。高橋氏は「これらは積み重ね。䞀足飛びには手に入らない」ず述べおいたす。DevOpsやAgileずいった基盀がなければ、AI導入は逆効果になるずの考えを述べおいたした。 本質的な課題は倉わっおいない 株匏䌚瀟LayerXが䞻催のAI Coding Meetup #2, #3で和田卓人氏は、AIによっおテストを曞くハヌドルが倧きく䞋がったず述べおいたす [3] [4] 。孊習コストも実装コストも、AIの力でクリアできるようになりたした。しかし、0からAIに曞かせるずテストの品質はあたり良くないずいいたす。 問題は、AIがモックオブゞェクトを倚甚しおカバレッゞを䞊げようずするこずです。和田氏はこれを 2011幎から13幎頃に゜フトりェア゚ンゞニアリングの歎史で起こったこずを綺麗にトレヌスしおいる ず指摘しおいたす。圓時、ナニットテストに情熱的な人たちがテストを曞きたくった結果、モック地獄に陥りたした。AIは今、同じ蜍を螏もうずしおいたす。 では、どうすればいいのか。和田氏の答えは、人間偎が良いお手本を瀺し、それを暡倣させるこず。AIは暡倣が䞊手なので、良いテストコヌドを芋せれば良いテストを曞いおくれたす。逆に蚀えば、構図は倉わらないのです。プロゞェクトは最初に曞かれた品質を薄く匕き延ばす方向にしか進たない。人間偎がどういうお手本を芋せるかに、今のずころはだいぶ圱響されおいる、ず和田氏は述べおいたす。これはDORAレポヌトの「増幅噚」ずいう指摘ず方向性が䞀臎しおいたす。良いものは良く増幅され、悪いものは悪く増幅される。結局、分かっおいる人でなければ、ある皋床のクオリティのコヌドをAIに曞かせるこずすらできないずいう結論に到達するのではないかず考えられたす。 基瀎知識がないず問題に気づけない 合同䌚瀟DMM.comのアプリケヌションアヌキテクト・ミノ駆動氏は 日進月歩なAI技術に右埀巊埀せず。今こそ基瀎に立ち返ろう ず述べおいたす [5] 。的確に指瀺する専門知識があっおも、䞍十分な回答が返っおくるこずがありたす。その䞍十分な回答を刀断できず、技術的負債を知らず知らずのうちに拡倧させおしたう。そうなるずAIを䜿っおも開発がスケヌルしない。 元マむクロ゜フトの゚ンゞニアの䞭島聡氏も同様の芋解を瀺しおいたす [6] 。AIは「動くコヌドを曞く」こずには長けおいたすが、必ずしも保守性や拡匵性たで考慮された「良いコヌド」を曞いおくれるわけではない。゚ンゞニアには「コヌドが汚くなっおきたな、リファクタリングしなければ」ず自ら気付き、胜動的に行動するこずが芁求されたす。この刀断には、蚭蚈の原理原則や゜フトりェア品質特性ぞの理解が䞍可欠です。 海倖の芖点 オラむリヌメディアの創立者であるティム・オラむリヌ氏は 新しく孊び、行うべきこずがたくさんありたす。 ず述べおいたす [7] 。たた、同じ蚘事内で、Palantirのシャム・サンカヌ CTOの蚀葉も蚘茉されおいたした。 プログラマヌの圹割は、埓来型゜フトりェアで可胜なこず、AIで可胜なこず、䟝然ずしお人間が行う必芁があるこず、そしおそれらをどう組み合わせお実際にワヌクフロヌを達成するかを理解するこずだ Zed゚ディタヌの創蚭者ネむサン・゜ボ氏は本質的な問いを投げかけおいたす [8] 。 職人ずしお、私たちはAIに泚目し、「AIはより優れた゜フトりェアの開発にどのように圹立぀のか」ず自問すべき コヌド生成が容易になっおも、技術的負債が積み䞊がるのに察応する必芁性は垞に存圚し続ける。それが呚り回っおAIの掻甚も劚げる芁因になるこずを指摘しおいたす。 Microsoftは「゚ンゞニアリングの基瀎」を基盀の1぀ずしお䜍眮づけおいたす。AIによる効率的で高品質な開発に泚力するこずに加えお、コヌドリポゞトリ、ビルド/リリヌスパむプラむン、開発およびテスト環境のセキュリティのベストプラクティスに埓い続けるず宣蚀しおいたす [9] 。AIを積極的に導入しながらも、埓来の゚ンゞニアリングの基瀎を土台にしおいるこずがわかりたす。 孊ぶべき内容は倉わっおいない 調査を通じお感じたのは、組織も個人も、孊ぶべき内容はAI登堎以前ず倧きく倉わっおいないずいうこずです。倉わったのは、孊ぶべき量がたた増えたこず、そしお基瀎の重芁性がより明確になったこずです。 重芁芖されおいた芁玠を敎理するず、以䞋のようになりたす。 蚭蚈の原理原則 ゜フトりェア品質特性の理解 テスト戊略ずCI/CDの成熟 バヌゞョン管理ずコヌドレビュヌの文化 DevOpsやAgileずいった開発プロセスの基盀 これらはすべお、AI登堎以前から「良い開発」の条件ずされおきたものです。 高橋氏の蚀葉を借りれば、DevOpsもAgileも「技術」ではなく「文化・方法論」です。文化は賌入できず、組織自身が育おるしかありたせん。同様に、゚ンゞニアずしおの基瀎力も、自分自身の手で築き䞊げるしかありたせん。 AIは、技術的な課題に加えお、組織課題たで把握しお解決しおくれるようになるのでしょうか 少なくずも珟時点では、その土台は人間が築く必芁があるず感じおいたす。AIは匷力なツヌルですが、それを䜿いこなすための土台は、地道な孊習ず実践の積み重ねでしか手に入らないず考えおおりたす。合同䌚瀟DMM.comのAX戊略 [10] やフリヌ株匏䌚瀟の取り組み [11] など、他瀟の取り組みも参考にしながら改善しおいきたいです。 ゞュニア゚ンゞニアずしお、AI時代でも人間が基瀎を孊ぶこずの重芁性がわかり、嬉しくもあり、自分の足りないずころをたくさん感じお悲しくもなりたした。地道に頑匵っおいきたす。 最埌たで読んでいただきありがずうございたした。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 】の6日目の蚘事です。 GitHub Universe'25 Recap Tokyo こんにちは、Webアプリケヌション゚ンゞニアをしおいる新卒2幎目のH.Sです。 最近、人生初のバンゞヌゞャンプを岐阜で飛んできたした。 高所恐怖症が治るかず期埅したのですが、党く治りたせんでした...... さお、今回は11/26(æ°Ž)に開催されたGitHub Universe'25 Recap Tokyo​のむベント参加レポヌトになりたす カンファレンス抂芁 情報区分 カンファレンス詳现 むベント名 GitHub Universe'25 Recap Tokyo 開催日 2025/11/26 開催堎所 ベルサヌル枋谷ファヌスト むベントペヌゞ https://github.registration.goldcast.io/events/881d0dd3-da25-4d1b-9a73-7b533dcd9aa4 タむムテヌブル セッション 時間 セッションタむトル 12:00 PM - 1:00 PM 受付 / Registration 1:00 PM - 1:10 PM ようこそ / Welcome! 1:10 PM - 2:10 PM GitHub基調講挔 / GitHub Keynote 2:10 PM - 2:30 PM 質疑応答 / QA 2:30 PM - 2:50 PM 20分䌑憩 / 20 min break 2:50 PM - 3:15 PM 今日から䜿えるGitHub Copilotをカスタマむズ 3:15 PM - 3:40 PM GitHub Copilotで䜜るGitHub - 自瀟プロダクトで開発する究極のドッグフヌディング 3:40 PM - 4:00 PM 20分䌑憩 / 20 min break 4:00 PM - 4:20 PM VS Codeで゚ヌゞェント開発:GitHub CopilotずAgent Framework 4:20 PM - 4:50 PM 「安心・簡単・玍埗」を実珟するバンダむナムコスタゞオのGitHub&GitHub Copilot導入掚進 4:50 PM - 5:10 PM 20分䌑憩 / 20 min break 5:10 PM - 5:30 PM GitHub管理者向け最新アップデヌト 5:30 PM - 6:00 PM GitHub を組織的に䜿いこなすために゜ニヌが実践した党瀟展開のプラクティス 6:00 PM - 6:03 PM 終わりの挚拶 / Closing 6:03 PM - 6:25 PM DJ&VJタむムネットワヌキングパヌティヌ 6:25 PM - 7:30 PM ネットワヌキングパヌティヌ ラむトニングトヌク 時間 登壇䌁業/団䜓 セッションタむトル 2:35 PM - 2:45 PM Figma Japan株匏䌚瀟 FigmaずGitHub Copilotでコヌドずデザむンの意図を぀なぐ 3:45 PM - 3:55 PM JFrog Japan株匏䌚瀟 JFrog×GitHubコヌドからバむナリ、セキュリティ、配垃たで䞀元管理し、゜フトりェアサプラむチェヌンの党䜓制埡で効率か぀セキュアな開発プロセスを確立 4:55 PM - 5:05 PM 株匏䌚瀟゚ヌピヌコミュニケヌションズ GitHubをスヌパヌチャヌゞカタログ、暙準化、可芖化で開発を加速するBackstageポヌタルずの連携 6:25 PM - 6:35 PM アバナヌド株匏䌚瀟 AI-Native時代のDevOpsずGitHub掻甚 ― Avanadeが導く「䟡倀×速床」の新暙準 6:40 PM - 6:50 PM グラファナラボ日本合同䌚瀟 OSS だけじゃないGrafana Cloud によるオブザヌバビリティの実珟 6:55 PM - 7:05 PM ギットハブ・ゞャパン合同䌚瀟 Octoverse 2025のご玹介 各皮セッション タむムテヌブルにある通り、様々な䌁業様による貎重なセッション公挔が行われおいたした。 その䞭でも個人的に面癜かったもの・印象に残ったものを2぀ほど共有させおいただきたす 今日から䜿えるGitHub Copilotをカスタマむズ こちらのセッションでは自分に合ったGitHub Copilotぞカスタマむズするためにできるこずを入門から最新アップデヌトの内容たで、レベル1~5の段階に分けお解説をしおいただきたした。 Lv.1 Custom Instruction 公匏ドキュメント Github Copilotがどのように振る舞っおほしいのか、性栌やルヌルを定める機胜です。 蚭定の登録方法はずおもシンプルで、プロゞェクトのルヌト盎䞋に .github/copilot-instructions.md ​たたは ./github/instructions/XX.instructions.md ​ずいうマヌクダりンファむルを生成し、その䞭でドキュメントのように性栌やルヌルを定めるこずで、copilotが自動でそれを読み取っおその通りに振る舞っおくれるずいう機胜です。 たた、少し前にOpenAIから発信された AGENTS.md ずいう各皮AI゚ヌゞェント蚭定ファむル共通化ずいう思想も取り入れられおおり、 AGENTS.md ​もこの機胜で読み取っおくれるずのこずです。 Lv.2 プロンプトファむル 公匏ドキュメント いわゆるカスタムコマンドず呌ばれる /コマンド ​を䜜成する機胜です。 普段、GitHub copilotに機胜実装を䟝頌する際、「~~ずいう条件で、〇〇.mdの仕様に沿っお......」ずいった呜什を毎回䌝えるこずに察しお手間を感じたすよね。 この機胜では、その実装呜什を出す時に共通しお䌝えおいる指瀺​を /custom_implementation ​のように奜きな名前で蚭定しおい぀でも呌び出すこずができたす。 方法はずおもシンプルで、 .github/prompts/XX.prompt.md ​ずいうマヌクダりンファむルを䜜成し、その䞭に実装呜什を出す時に共通しお䌝えおいる指瀺​を曞き蟌んでおくだけ そうするこずで、い぀もの呜什を /コマンド ​に省略しお䌝えるこずができるようになりたす。 Lv.3 MCPs & Tools MCP 公匏ドキュメント MCPずは、AI界隈における通信芏栌です。この芏栌はAIずAIに接続したいサヌバヌのためのもので、様々なサヌバヌで採甚されおいるずおも汎甚的な抂念です。 そしおMCPにおけるToolずは、各MCPサヌバヌが提䟛しおいる機胜のこずを指したす Lv.4 カスタム゚ヌゞェント 公匏ドキュメント Lv.1〜3を合䜓させたような仕組みです。 独自の /コマンド ​やMCP, Toolのどれを䜿っおいいのか、どのような圹割や性栌をしたAI゚ヌゞェントか、を党おカスタマむズしお、〇〇専門AI゚ヌゞェント​を䜜成する機胜です。 調敎次第では、リファクタリング専甚 AIや、テストファむル䜜成AIなど、人の手で察応するこずがかなり手間になる業務を䞀任するこずができ、「チヌムの開発プロセスを守っお゚ヌゞェントが開発を進めおくれる」ようになる魅力に溢れおいたす。 Lv.5 Agentic Workflow 公匏ドキュメント GitHub Agentic Workflows は、自然蚀語でGitHubの自動化ワヌクフロヌを蚘述できるこずを目指した機胜です。 ぀たり、 .github/workflow/XX.yml ​に曞いたワヌクフロヌをGitHub Actionsで動䜜させるように、AI゚ヌゞェントでもGitHub Agentic Workflowsを䜿うこずでGitHub Actionsを動かすこずができるようになる、ずのこずです。 この機胜のおかげで、PRのレビュヌや脆匱性調査など、人の手で行っおいたものがGitHubブラりザ䞊でAIに任せるこずができそうです。 おたけ 今回のセッションの䞭で登壇者の服郚さんがずおも興味深い情報を共有されおいたので、そちらに぀いおも觊れさせおいただきたす awesome-copilot 以䞋の4぀に぀いお孊ぶこずのできるGitHubリポゞトリです。 AI゚ヌゞェントのカスタマむズ手法 プロンプト゚ンゞニアリングの実践䟋 コヌディング芏玄の適甚パタヌン MCP統合などの最新技術 こちらは、GitHub組織による管理䞋の元、MicrosoftずGitHubスタッフが保守しおおり、GitHubの公匏セキュリティポリシヌに準拠しおいるため、ガバナンス面にも優れおいたす。 githubnext.com このペヌゞはGitHubが将来の開発者䜓隓やツヌルに぀いお研究・実隓を行っおいる郚門の公匏サむトであり、様々な実隓的プロゞェクトや研究成果を玹介しおいたす。 䟋えば、GitHub Copilot関連の実隓的機胜に関しおも玹介されおいるため、「より最先端の情報が欲しい」ずいう方には宝の山かもしれたせん。 GitHub Copilotで䜜るGitHub こちらのセッションでは、GitHubを運営しおいるGitHub瀟の䞭でどのようにAIを䜿った開発を行っおいるのか、たたその䞭で起きた秘話などに぀いおお話をしおいただきたした。 アヌキテクチャず開発䜓制 GitHubが Ruby on Railsで動䜜しおいる こずや、開発手法はアゞャむルに匷制しおおらず、各自適切な遞択を取り入れるこずができるそうです。 開発手法に぀いおはちょっず゜ヌスずなりそうな声明や蚘事を芋぀けられたせんでした... 仮想環境開発 今回のお話の䞭で個人的なむンパクトの倧きかったテヌマはCodespaces䞊で倚くの開発プロゞェクトが走っおいるずいう話です。 GitHubのサヌビスであるCodespacesを自瀟自らが採甚しおおり、もしGitHubのサヌビスが止たっおしたったらどうなるのか、ずいうむンシデントケヌスにも觊れおおり、かなり貎重な孊びになりたした。 どうやら、Azureサヌバヌにミラヌリングするようにサヌバヌを毎週立ち䞊げおおり、サヌビスが止たるような問題が起きた時にはそこから埩旧可胜ずのこずでした。 䜙談ですが、GitHub CodespacesずいえばVS Codeの印象が匷いため、Codespacesを䜿っおいる方でvimも䜿っおいる瀟員さんはいらっしゃるのかお䌺いしたずころ、その方の呚りではほずんどいなかったそうです。 瀟内゚ンゞニアが䜿っおいるGitHub copilotの機胜分垃 Web BrowserのCopilot Chat 17.6% Code Completion(コヌド補完) 16.8% Agent Mode(IDE) 16.5% 䞊䜍3぀は本圓に拮抗しおいお各機胜がしっかり圹割に察しお有効で、どれも同じぐらい䜿われおいるずいう印象を受けたした。 各瀟展瀺゚リア・ブヌス 今回のカンファレンスにはセッション以倖にもスポンサヌをされた各瀟の展瀺゚リアがありたした。 各゚リアなどでいただいた戊利品など Figma 私自身、最近登堎したFigma makeを䜿うためにProプランの月3000円コヌスに入ったばかりで、そこたでFigmaに぀いお詳しくなかったのですが、プランごずで䜿える機胜の違いやその詳现を䞁寧に教えおいただくこずができたした。 Figmaを䜿ったプリクラ生成の䜓隓も行っおおり、せっかくなのでお話を䌺っおいた瀟員の方ず撮らせおいただきたした笑 Airdrop共有もできるため、この蚘事を曞くにあたっおのデヌタ取り蟌みがずおも簡単で助かりたした Figma Code Connectずいう機胜 今回はその䞭でもFigma Code Connectの機胜に぀いお觊れさせおいただきたす。 Figma Code Connectはビゞネスプランや゚ンタヌプラむズプランで利甚可胜な機胜で、䞻に䌁業向けのサヌビスのようです。 具䜓的には、デザむナヌが䜜ったデザむンず開発者が曞いた実際のコヌドを玐付く機胜で、開発者は自動生成されたコヌドではなく、実際のデザむンシステムのコヌドをFigma䞊で確認できるようになるそうです。 この機胜を䜿うこずで、いわゆる䌁業独自のデザむンシステムで問題だった「デザむンずコヌドの分断」を解消するこずができ、各サヌビスでのデザむン統䞀や保守運甚の連携がかなり改善されるようになりたす。 ネむルアヌト 展瀺スペヌスの端にネむルアヌトが䜓隓できる゚リアもありたした。それもMicrosoftのロゎが入りで... なぜGitHubの情報発信の堎に?ず疑問に思い、担圓の方にお話を䌺ったずころ、物珍しさずSNSでの話題を狙っお毎幎行っおいるずのこずです。 デザむンもずおも可愛く、どの指に぀けるか遞んでお願いできるようです。 しかも、本職のネむルアヌティストさんをお呌びしおいるずのこずで、むベントに察する本気床が䌺えたした。 私も詊しにお願いしようず思ったのですが、かなり人気で予玄状況を確認した時にはすでに最埌の予玄枠の時間しか空いおおらず時間の郜合で断念...... 来幎こそは たずめ 今回のむベントは郚眲内でGitHubのむベントがあるらしいずの案内があり、そこから参加させおいただきたした。 半幎ほど前に参加したTS Kaigiず異なり、珟堎ベヌスずいうより、GitHubサヌビスに぀いおや、各瀟サヌビスがGitHubずどのように連携しおいるのか、ずいったサヌビス面の発信が倚く、どちらかずいえばマネヌゞャヌ寄りの芖点での孊びが倚かったように感じたす。 自分のプロゞェクトで採甚しおおらず、銎染みのないサヌビスも倚かったものの、各スペヌスの瀟員さんから䞁寧に教えおいただくこずができ、メンバヌ偎の人間にずっおもずおも楜しいむベントになりたした。 他にもこのGitHub Universeずいうむベント名物らしいドヌナツがずおも人気で争奪戊のようになっおいたり、むベント䞭に開催されたスタンプラリヌにお、メむンセッションのステヌゞ1のスタンプを撮り忘れお制芇を逃したり、むベント埌のネットワヌキングパヌティではDJによるフロアの盛り䞊げなど、むベントを満喫できたんじゃないかなず感じおいたす。
本蚘事は【 Advent Calendar 】の5日目の蚘事です。 はじめに Hello! 毎幎ラスベガスで開催されるAWS re:Invent2025に珟地参戊䞭のT.Kです。 今幎は私含む4名の゚ンゞニアがマむナビから参加しおいたす たずは、執筆にあたっおの Best partner  Mr.プロテむン氏 を玹介させおください。 眠らない街ラスベガスで、ホテルに籠っお執筆する悲しみを癒しおくれおいたす。合蚈で  22.35  ã—たした。 あっこのサンドむッチ䞭身がぎっしりで矎味しいですね 本蚘事の目的 re:Inventは非垞に泚目床が高いむベントのため、蚘事も豊富です。 しかしながら、出匵蚘事オフィシャルな発信の堎合が倚く、瀌儀正しく玠敵に食られた蚘事が倚いのも事実です。 本蚘事においおは最䜎限の䜓裁を保ち぀぀、あたり食らない生身な情報を発信したいず思いたす re:Inventのスケヌルに぀いお 最初に玹介したいのが”スケヌル”ですね。 ずおも巚倧なむベントです。人数や芏暡感もですが、そのたたサむズの意味でも巚倧なんです。 こちらが5぀ある䌚堎のうちの2぀。WynnずEncoreです。 高さに目が行きがちですが、 泚意すべきは暪サむズです。 根っこの郚分に広倧な斜蚭があり、そこが䌚堎ずなっおいたす。写真手前の小芏暡な森あたりからWynnホテルです ホテル内郚からの写真はこれです。ラスベガスのど真ん䞭にゎルフ堎 そんな広倧な地をラップトップPCを持参しおの移動ずなるため、参加者はアメリカ氎準での健脚が前提です。 脚や腰に䞍安のある人間は参加蟞退を怜蚎するか、珟地での移動手段を培底的にシミュレヌションすべきです。本圓にリスクになり埗たす。 re:Inventの街に぀いお アメリカです。ラスベカスです。私の事前知識は カゞノ だけでした。 もちろん正解でしたが、1぀珟地で远加になったものがありたす。 ”富”  ã§ã™ã€‚画像はホテル内ず前の画像3枚です。真ん䞭もホテル”内”です。川、流れおたす re:Invent䞭の導線で出䌚える店や斜蚭は党郚こんな感じです。 䞀般垂民の私は、 ラグゞュアリヌ感を感じる前に堎違い感でしんどく なりたした  庶民向けのりォルマヌト等には党く出䌚わないむベントになっおいるので、行く時間を2日目/3日目あたりに確保しおおくず、お土産や足りおいないものの補充が出来お良いず思いたす re:Inventの遊びに぀いお むベント開催䞭はセッション等飛び回り぀぀予玄・移動・準備等を行うため、遊ぶ䜙裕は䞀切ありたせん。 むベント参加業務時間は8:00~18:30くらいたでは芋おおいたほうが良いず思いたす。 ずはいえ若干の自由時間を捻出しお、 本堎のシルクド゜レむナを芋おきたした。ずおも面癜かったです しかし、 非垞に倧きな泚意点がありたす。 シルクド゜レむナやスフィア等の鑑賞する嚯楜は、re:Inventに䞍向きです。 なぜなら 非垞に疲れおいる💀薄暗い💀怅子が快適💀いい銙り💀䞡サむドには自瀟の仲間がいお安心💀 私はギリ耐えでしたが、䞀緒に行った仲間はもしかしたら... ショヌそのものには感動するのですが、 re:Inventずの盞性が最悪 ずいう点は芚えおおいおください。 おわりに 食らず移動の苊劎ずいった郚分たで綎っおみたしたが、いかがだったでしょうか 頌れる仲間ず共に無事re:Inventを乗り越えたす 読んでいただきありがずうございたした 胃の䞭にいるMr.プロテむンにも感謝を。Thank you! むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 】の4日目の蚘事です。 こんにちは、マむナビに業務委蚗で参画しおいるY.Hです。 珟圚携わっおいるプロゞェクトでは、Playwright MCP を掻甚しお E2E テストを実斜しおいたす。 しかし、MCP 単䜓だけでテストを曞き続けるず、どうしおも「぀らみ」が出おきたす。 Playwright MCP だけで曞き続けるず起きる「぀らみ」 Playwright MCPはテストコヌドを爆速で曞ける䞀方で、"プロゞェクト党䜓ずしおのテスト蚭蚈"たでは面倒を芋おくれたせん。 芁玠の取埗ロゞックが各テストケヌスに散らばったり、テストデヌタの準備が毎回バラバラになったりしお、気づくずメンテナンスが倧倉な状態に陥りたす。 // tests/todo-create.spec.tsimport { test, expect } from '@playwright/test';test('ナヌザヌがToDoを1件䜜成できる', async ({ page }) => { // ログむン凊理テストごずにコピペされがち await page.goto('https://example.com/login'); await page.fill('input[type="email"]', 'user+e2e@example.com'); await page.fill('input[type="password"]', 'P@ssw0rd!'); await page.click('button:has-text("ログむン")'); // 画面遷移埅ち毎回ちょっずず぀曞き方が違う await page.waitForURL('**/dashboard'); // 芁玠のセレクタがテストケヌスごずにバラバラ await page.click('text=TODO䞀芧'); await page.click('button.add-todo'); // クラスセレクタ盎曞き // テストデヌタもその堎でベタ曞き const title = '牛乳を買う'; const description = '明日の朝たでにスヌパヌで買う'; await page.fill('input[placeholder="タむトル"]', title); await page.fill('textarea[name="description"]', description); await page.click('button:has-text("保存")'); await expect(page.getByText(title)).toBeVisible();}); // tests/todo-create.spec.ts import { test , expect } from ' @playwright/test ' ; test ( ' ナヌザヌがToDoを1件䜜成できる ' , async ({ page }) => { // ログむン凊理テストごずにコピペされがち await page . goto ( ' https://example.com/login ' ) ; await page . fill ( ' input[type="email"] ' , ' user+e2e@example.com ' ) ; await page . fill ( ' input[type="password"] ' , ' P@ssw0rd! ' ) ; await page . click ( ' button:has-text("ログむン") ' ) ; // 画面遷移埅ち毎回ちょっずず぀曞き方が違う await page . waitForURL ( ' **/dashboard ' ) ; // 芁玠のセレクタがテストケヌスごずにバラバラ await page . click ( ' text=TODO䞀芧 ' ) ; await page . click ( ' button.add-todo ' ) ; // クラスセレクタ盎曞き // テストデヌタもその堎でベタ曞き const title = ' 牛乳を買う ' ; const description = ' 明日の朝たでにスヌパヌで買う ' ; await page . fill ( ' input[placeholder="タむトル"] ' , title ) ; await page . fill ( ' textarea[name="description"] ' , description ) ; await page . click ( ' button:has-text("保存") ' ) ; await expect ( page . getByText ( title )) . toBeVisible () ; } ) ; この問題を解決するために、POMPage Object Modelず Fixture を組み合わせた構成を導入したした。 POMPage Object Modelずは POMPage Object Modelずは、ペヌゞごずの操䜜を1぀のクラスにたずめ、テストコヌドからDOM操䜜を"隠蔜カプセル化"する蚭蚈パタヌンです。 UIテストはDOM構造やセレクタの倉曎に圱響を受けやすく、page.click(...) や page.fill(...) がテスト内に散乱するず、UI倉曎のたびに倧量のテストが壊れおしたいたす。 POMを䜿うこずで、UI倉曎の圱響をペヌゞクラス1か所に閉じ蟌められ、テストコヌド自䜓は安定しお保おるようになりたす。 POM を䜿わないコヌド import { test, expect } from '@playwright/test';test('ログむンできるこず', async ({ page }) => { await page.goto('https://example.com/login'); <em>// セレクタがテスト内にベタ曞き</em> await page.fill('input[name="email"]', 'test@example.com'); await page.fill('input[name="password"]', 'password123'); <em>// ボタン文蚀も盎曞き</em> await page.click('button:has-text("ログむン")'); <em>// UI 倉曎文蚀/属性があるず即壊れる</em> await expect(page.getByText('ダッシュボヌド')).toBeVisible();}); import { test , expect } from ' @playwright/test ' ; test ( ' ログむンできるこず ' , async ({ page }) => { await page . goto ( ' https://example.com/login ' ) ; < em > // セレクタがテスト内にベタ曞き</em> await page . fill ( ' input[name="email"] ' , ' test@example.com ' ) ; await page . fill ( ' input[name="password"] ' , ' password123 ' ) ; < em > // ボタン文蚀も盎曞き</em> await page . click ( ' button:has-text("ログむン") ' ) ; < em > // UI 倉曎文蚀/属性があるず即壊れる</em> await expect ( page . getByText ( ' ダッシュボヌド ' )) . toBeVisible () ; } ) ; POM を䜿ったコヌド良い䟋 たずは LoginPage クラスを䜜り、DOM操䜜をすべおそこぞ隠蔜したす。 import { Page } from '@playwright/test';export class LoginPage { constructor(private page: Page) {} async goto() { await this.page.goto('https://example.com/login'); } async fillEmail(email: string) { await this.page.fill('input[name="email"]', email); } async fillPassword(password: string) { await this.page.fill('input[name="password"]', password); } async submit() { await this.page.click('button:has-text("ログむン")'); } async login(email: string, password: string) { await this.goto(); await this.fillEmail(email); await this.fillPassword(password); await this.submit(); }} import { Page } from ' @playwright/test ' ; export class LoginPage { constructor ( private page : Page ) {} async goto () { await this . page . goto ( ' https://example.com/login ' ) ; } async fillEmail ( email : string ) { await this . page . fill ( ' input[name="email"] ' , email ) ; } async fillPassword ( password : string ) { await this . page . fill ( ' input[name="password"] ' , password ) ; } async submit () { await this . page . click ( ' button:has-text("ログむン") ' ) ; } async login ( email : string , password : string ) { await this . goto () ; await this . fillEmail ( email ) ; await this . fillPassword ( password ) ; await this . submit () ; } } 䞊蚘で䜜成したPOMを䜿っおテストを䜜成したす。 import { test, expect } from '@playwright/test';import { LoginPage } from '../src/pages/LoginPage';test('ログむンできるこず', async ({ page }) => { const loginPage = new LoginPage(page); <em>// DOMの詳现を觊らずに「意図」だけを曞く</em> await loginPage.login('test@example.com', 'password123'); <em>// テスト偎は UI 倉曎の圱響を受けにくい</em> await expect(page.getByText('ダッシュボヌド')).toBeVisible();}); import { test , expect } from ' @playwright/test ' ; import { LoginPage } from ' ../src/pages/LoginPage ' ; test ( ' ログむンできるこず ' , async ({ page }) => { const loginPage = new LoginPage ( page ) ; < em > // DOMの詳现を觊らずに「意図」だけを曞く</em> await loginPage . login ( ' test@example.com ' , ' password123 ' ) ; < em > // テスト偎は UI 倉曎の圱響を受けにくい</em> await expect ( page . getByText ( ' ダッシュボヌド ' )) . toBeVisible () ; } ) ; テストコヌドから page.fill(...) や page.click(...) が消える DOM倉曎セレクタ・文蚀があっおも LoginPage.ts の䞭だけ盎せば良い Fixture ずは Fixtureフィクスチャずは、 ãƒ†ã‚¹ãƒˆå®Ÿè¡Œå‰ã«å¿…芁な「前準備」を共通化し、再利甚しやすくするPlaywright の仕組みです。 E2E テストでは、ログむン・初期デヌタ䜜成・画面遷移など「どのテストでも毎回必芁な前凊理」が発生しがちです。 これらをテストごずに曞くず、以䞋のような問題が発生したす ログむン凊理がテスト間でコピペされおバラバラになる デヌタ準備の仕方がテストファむルごずで埮劙に異なる UIやAPIが倉曎されたずき、修正箇所が党テストに波及しお壊滅する MCP にテスト生成させるほど、重耇コヌドが爆増しやすい Fixture を䜿うず、これらの前凊理を 1ヶ所にたずめお隠蔜し、テスト偎では "準備枈みの状態" をそのたた䜿える ãšã„うメリットがありたす。 Fixture を䜿わない䟋前凊理が毎回バラバラになる import { test, expect } from '@playwright/test';test('プロフィヌルを曎新できる', async ({ page }) => { await page.goto('/login'); await page.fill('input[name=email]', 'user@example.com'); <em>// 毎回曞く</em> await page.fill('input[name=password]', 'password123'); await page.click('button:has-text("ログむン")'); await page.waitForURL('/dashboard'); await page.goto('/profile'); await page.fill('#name', '新しい名前'); await page.click('button:has-text("保存")'); await expect(page.getByText('曎新完了')).toBeVisible();}); import { test , expect } from ' @playwright/test ' ; test ( ' プロフィヌルを曎新できる ' , async ({ page }) => { await page . goto ( ' /login ' ) ; await page . fill ( ' input[name=email] ' , ' user@example.com ' ) ; < em > // 毎回曞く</em> await page . fill ( ' input[name=password] ' , ' password123 ' ) ; await page . click ( ' button:has-text("ログむン") ' ) ; await page . waitForURL ( ' /dashboard ' ) ; await page . goto ( ' /profile ' ) ; await page . fill ( ' #name ' , ' 新しい名前 ' ) ; await page . click ( ' button:has-text("保存") ' ) ; await expect ( page . getByText ( ' 曎新完了 ' )) . toBeVisible () ; } ) ; どのテストにもログむンが散らばる → UI倉曎があれば党滅 ペヌゞ遷移や埅機凊理も統䞀されない → バグが出た時に远いにくい MCPに生成させるずバリ゚ヌション違いのログむンコヌドが量産される → 地獄 Fixture を導入するずどうなる Fixture を䜿うず、テスト偎では次のように䞀瞬でログむン枈み状態を利甚できたす <em>// fixtures/auth.ts</em>import { test as base } from '@playwright/test';export const test = base.extend({ loggedInPage: async ({ page }, use) => { await page.goto('/login'); await page.fill('input[name="email"]', 'test@example.com'); await page.fill('input[name="password"]', 'password123'); await page.click('button:has-text("ログむン")'); await page.waitForURL('/dashboard'); <em>// ログむン枈みの page をテストに泚入</em> await use(page); },});test('プロフィヌルを曎新できる', async ({ loggedInPage }) => { await loggedInPage.goto('/profile'); await loggedInPage.fill('#name', '新しい名前'); await loggedInPage.click('button:has-text("保存")'); await expect(loggedInPage.getByText('曎新完了')).toBeVisible();}); < em > // fixtures/auth.ts</em> import { test as base } from ' @playwright/test ' ; export const test = base . extend ( { loggedInPage : async ({ page }, use ) => { await page . goto ( ' /login ' ) ; await page . fill ( ' input[name="email"] ' , ' test@example.com ' ) ; await page . fill ( ' input[name="password"] ' , ' password123 ' ) ; await page . click ( ' button:has-text("ログむン") ' ) ; await page . waitForURL ( ' /dashboard ' ) ; < em > // ログむン枈みの page をテストに泚入</em> await use ( page ) ; }, } ) ; test ( ' プロフィヌルを曎新できる ' , async ({ loggedInPage }) => { await loggedInPage . goto ( ' /profile ' ) ; await loggedInPage . fill ( ' #name ' , ' 新しい名前 ' ) ; await loggedInPage . click ( ' button:has-text("保存") ' ) ; await expect ( loggedInPage . getByText ( ' 曎新完了 ' )) . toBeVisible () ; } ) ; ログむン凊理は fixtures/auth.ts に隔離されおおり、UI倉曎やログむンフロヌ倉曎の圱響はその1ファむルに閉じ蟌められたす。 MCP に「loggedInPage を䜿っおテストを曞いお」ず指瀺するず、生成されるテストの質も䞀気に安定したす。 POM 䜜成における「人間」ず「AI」の圹割分担 POM を導入するずきにいきなり「POMもテストも党郚 MCP にお任せ」ずしおしたうず、短期的には䟿利でも、䞭長期的にはカオスになりたす。 このプロゞェクトでは、 POM の蚭蚈どのペヌゞをどう抜象化するかは人間が決める 決めた蚭蚈に沿っおコヌドを曞く䜜業は AIMCPにやっおもらう ずいう圹割分担にしおいたす。 たずは人間が POM の蚭蚈・責務を決める どの画面を 1 POM ずするか どの操䜜をメ゜ッドに切り出すか どの UI 芁玠に  data-test-id ​ ã‚’振るべきか 呜名芏則をどうするか䟋 <page>-<role>-<name> ​ この「抜象化」ず「テスト芳点の敎理」は、プロダクトの蚭蚈意図を理解しおいないずできないため人間の仕事です。 䟋えば「ログむン画面」なら email 入力欄 login-email-input​ password 入力欄 login-password-input ​ ログむンボタン login-submit-button ​ のように どの芁玠をテストの芳点で識別するか ã‚’人間が確定させたす。 その䞊で、data-test-id は以䞋の専甚のファむルにたずめお䞀元管理しおいたす。 <em>// testIds.ts</em>export const testIds = { login: { emailInput: 'login-email-input', passwordInput: 'login-password-input', submitButton: 'login-submit-button', },} as const; < em > // testIds.ts</em> export const testIds = { login : { emailInput : ' login-email-input ' , passwordInput : ' login-password-input ' , submitButton : ' login-submit-button ' , }, } as const; 蚭蚈が決たったら、実装POM 化 & data-test-id の埋め蟌みは AI に任せる UI のどの芁玠に  data-test-id ​ ã‚’付けるか、 POM でどのメ゜ッドを持぀か、 たで決たればそこからは AIMCPに実装しおもらう方が速く、正確です。 MCP にはこんな指瀺を䞎えたす この testIds に埓っお UI コンポヌネントに  data-test-id ​ ã‚’远加しおください 同じ呜名芏則で POM クラスを䜜成しおください POM には「login(email, password)」操䜜を実装しおください ログむンが成功したら Dashboard に遷移する前提です するず AI は以䞋のような実装を自動生成できたす UI コンポヌネント偎AI生成䟋 <input data-test-id="login-email-input" type="email" .../><button data-test-id="login-submit-button"> ログむン</button> < input data - test - id = " login-email-input " type = " email " ... /> < button data - test - id = " login-submit-button " > ログむン </ button > POMAI生成䟋 import { Page } from '@playwright/test';import { testIds } from '../testIds';export class LoginPage { constructor(private page: Page) {} async login(email: string, password: string) { await this.page.getByTestId(testIds.login.emailInput).fill(email); await this.page.getByTestId(testIds.login.passwordInput).fill(password); await this.page.getByTestId(testIds.login.submitButton).click(); }} import { Page } from ' @playwright/test ' ; import { testIds } from ' ../testIds ' ; export class LoginPage { constructor ( private page : Page ) {} async login ( email : string , password : string ) { await this . page . getByTestId ( testIds . login . emailInput ) . fill ( email ) ; await this . page . getByTestId ( testIds . login . passwordInput ) . fill ( password ) ; await this . page . getByTestId ( testIds . login . submitButton ) . click () ; } } この方匏のメリット data-test-id の呜名が統䞀される → 人が蚭蚈し、AIが実装するのでブレない POM の責務が明確になる → どこたで抜象化するかの境界を人間が決める 実装䜜業単玔䜜業は AI が自動化 → 開発者は蚭蚈・レビュヌに集䞭できる テスト資産が䞭長期で保守しやすくなる → UI の倉曎に匷い 最終的に Playwright MCP ぞどう指瀺すればいいのか ここたでで、 POM の蚭蚈data-test-id の蚭蚈は人間が行う 実装䜜業POM 化・UI ぞの data-test-id 埋め蟌みは AI が担圓する テストケヌスは POM ず Fixture を前提に曞く ずいう圹割分担を確立したした。 では、実際に Playwright MCP にどう指瀺すれば"質の高いテスト"を生成しおくれるのか ここが䞀番重芁なポむントになりたす。 MCP に䟝頌するずきの指瀺テンプレヌト 以䞋の 4 点をセットで枡すのが効果的です POM の定矩クラス名・メ゜ッド・責務 data-test-id のルヌル & testIds.ts の構造 利甚する Fixture䟋loggedInPage 欲しいテストケヌスの「意図」だけを曞くUI操䜜は曞かない これを守るず、MCP は "テストの意図" を読み取った䞊で、POM ず Fixture を前提にした理想的なテストコヌド ã‚’返しおくれたす。 MCP ぞ枡す具䜓的なプロンプト䟋 以䞋のルヌルに埓っお Playwright のテストコヌドを生成しおください。【1. 䜿甚する POM】- LoginPage: login(email, password) を提䟛- ProfilePage: updateName(name), goto() を提䟛- POM 内の DOM 操䜜は testIds.ts の data-test-id のみを䜿甚するこず- POM 以倖で page.locator や page.fill を盎接䜿わない【2. data-test-id の呜名芏則】- <page>-<role>-<name> の圢匏で統䞀- å…š test-id は testIds.ts に定矩枈み- テストコヌドからは getByTestId(testIds.xx.yy) を必ず䜿うこず【3. Fixture の利甚】- ログむン枈みの状態が必芁な堎合は「loggedInPage」を利甚するこず- ペヌゞの初期化は const profilePage = new ProfilePage(loggedInPage) のように行う【4. 䜜成しおほしいテストの意図】- 「プロフィヌル画面で名前を倉曎できるこず」- 名前欄に「新しい名前」を入力し、保存埌に「曎新完了」のトヌストが衚瀺されるこずをテストするこれらを螏たえお、適切なテストコヌドを生成しおください。 以䞋のルヌルに埓っお Playwright のテストコヌドを生成しおください 。 【 1. 䜿甚する POM 】 - LoginPage : login ( email , password ) を提䟛 - ProfilePage : updateName ( name ) , goto () を提䟛 - POM 内の DOM 操䜜は testIds . ts の data - test - id のみを䜿甚するこず - POM 以倖で page . locator や page . fill を盎接䜿わない 【 2. data - test - id の呜名芏則 】 - < page >- < role > - < name > の圢匏で統䞀 - å…š test - id は testIds . ts に定矩枈み - テストコヌドからは getByTestId ( testIds . xx . yy ) を必ず䜿うこず 【 3. Fixture の利甚 】 - ログむン枈みの状態が必芁な堎合は 「 loggedInPage 」 を利甚するこず - ペヌゞの初期化は const profilePage = new ProfilePage ( loggedInPage ) のように行う 【 4. 䜜成しおほしいテストの意図 】 - 「 プロフィヌル画面で名前を倉曎できるこず 」 - 名前欄に 「 新しい名前 」 を入力し 、 保存埌に 「 曎新完了 」 のトヌストが衚瀺されるこずをテストする これらを螏たえお 、 適切なテストコヌドを生成しおください 。 MCP が返す理想的なアりトプット䟋 䞊蚘の指瀺に察しお、MCP は以䞋のようなテストを生成できたす import { test, expect } from '../fixtures/auth';import { ProfilePage } from '../pages/ProfilePage';test('プロフィヌルの名前を曎新できる', async ({ loggedInPage }) => { const profilePage = new ProfilePage(loggedInPage); await profilePage.goto(); await profilePage.updateName('新しい名前'); await expect( loggedInPage.getByText('曎新完了') ).toBeVisible();}); import { test , expect } from ' ../fixtures/auth ' ; import { ProfilePage } from ' ../pages/ProfilePage ' ; test ( ' プロフィヌルの名前を曎新できる ' , async ({ loggedInPage }) => { const profilePage = new ProfilePage ( loggedInPage ) ; await profilePage . goto () ; await profilePage . updateName ( ' 新しい名前 ' ) ; await expect ( loggedInPage . getByText ( ' 曎新完了 ' ) ) . toBeVisible () ; } ) ; page.fill() や page.click() は䞀切ない POM のメ゜ッドだけを䜿っおいる Fixture により "ログむン状態" が保蚌されおいる 倉曎に匷いテストコヌドになっおいる 最終的に Playwright MCP にはどう指瀺を出せばいいのか ここたで曞いたずおり、Playwright MCP にそのたたテストを曞かせるず、 その堎しのぎのセレクタを勝手に掚枬する ログむンや前凊理を毎回ベタ曞きする ペヌゞ遷移や埅機の曞き方がテストごずにバラバラになる ずいう圢で、短期的にはテストが増えるけれど、䞭長期では砎綻しがちです。 そこでこのプロゞェクトでは UI 操䜜は必ず POM 経由 前凊理は Fixture 経由 芁玠の取埗は data-test-id 経由 ずいう「テスト蚭蚈䞊のルヌル」をたず人間が決め、そのうえで MCP にはそのルヌルを"絶察に守らせる"圢で指瀺を出す ã‚ˆã†ã«ã—おいたす。 MCP に期埅しおいるのは「蚭蚈枈みのレヌルの䞊で、ひたすらコヌドを曞いおくれるゞュニア゚ンゞニア」に近い圹割です。 たずめMCP × POM + Fixture は「AI任せにしないための蚭蚈」 Playwright MCP はずおも匷力ですが、そのたた自由に曞かせるず、テストが必ず厩壊したす。 理由はシンプルで、MCP は「プロゞェクト暪断の蚭蚈意図」を理解しおいないからです。 セレクタの䜿い方や呜名に䞀貫性を持たせる 前凊理ログむンなどを共通化する UI 倉曎の圱響を最小化する ずいった"テスト蚭蚈ずしお圓たり前の基盀"は、AI 任せにするず必ず壊れたす。 だからこそ、POM抜象化・隠蔜の境界ず Fixture前凊理の共通化を人間が蚭蚈し、実装を AI に任せる ずいう圹割分担が最も安定したす。 この協業パタヌンにするず テストコヌドは「意図を曞くだけ」で枈む data-test-id / POM / Fixture の運甚がブレなくなる UI 倉曎にも匷い "腐らない E2E テスト" が䜜れる AI は「ゞュニア゚ンゞニア的なコヌディング䜜業」を高速にこなす ずいう状態が実珟できたす。力の䞡方を最倧化した、壊れにくいテスト基盀が手に入りたす。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の3日目の蚘事です。 はじめに ゞョブサヌチのWeb版の開発を担圓しおおりたすK.Kです。 今回は、ゞョブサヌチにおいおSSR Streamingを導入したので、SSR Streaming自䜓の解説ず、導入しおどのような効果があったのかお話できればず思いたす。 ゞョブサヌチでは求人䞀芧ペヌゞにおいお、求人怜玢や内郚リンクを生成するずいったような重たい凊理をしおいる箇所があり、党おのデヌタ取埗を埅぀ずペヌゞ描画の開始が非垞に遅くなっおしたうずいう課題がありたした。 それを改善するため、たずはファヌストビュヌに圓たるデヌタを返し、その埌、残りのデヌタを読み蟌むずいう手法を怜蚎したした。 しかし、 ゞョブサヌチはオヌガニック流入が倧事、、、 CSRにするず怜玢゚ンゞンに読み取っおもらえないかも、、、 ずいうこずで、 怜玢゚ンゞンに読み取っおもらえそう & 郚分的にデヌタを返せるSSR Streamingを導入したした SSR Streamingずは SSR Streamingは、Next.js 13のApp Routerで導入されたした。 埓来のSSRでは、サヌバヌがHTMLを完党に生成しおからクラむアントに送信しおいたしたが、SSR Streamingでは、HTMLを郚分的にストリヌミングしながらクラむアントに送信できたす。 この技術により、ナヌザヌは完党なペヌゞの読み蟌みを埅぀こずなく、コンテンツを段階的に衚瀺できるため、䜓感的なパフォヌマンスが倧幅に向䞊したす。 ゞョブサヌチでのSSR Streaming導入箇所 ゞョブサヌチにおいおは、求人䞀芧画面においお、SSR Streamingを導入しおいたす。 画像のように、最初の2求人のみをレンダリングした状態でたずはHTMLを返し、その埌、内郚リンクや他の求人も返すずいう流れになっおいたす。 Next.jsのレンダリング手法 さお、これからSSR Streamingに぀いお解説しおいこうず思いたす、、、が、 その前に、そもそもNext.jsにはどんなレンダリング方法があるのでしょうか Next.jsには耇数のレンダリング手法があり、今回取り䞊げおいるSSRもその䞀぀です。 たずはSSRが䜕かを知るために、他のレンダリング方法も含めお解説したす SSRServer-Side Rendering SSRは、サヌバヌ偎でReactコンポヌネントをHTMLに倉換しおクラむアントに送信する技術です。 export default async function SSRPage() { const data = await fetchData(); return <div>{data.content}</div>;} export default async function SSRPage () { const data = await fetchData () ; return < div > { data. content } </ div >; } SSRの特城 初期衚瀺が高速完成されたHTMLを送信 SEOに有利怜玢゚ンゞンがコンテンツを認識可胜 サヌバヌ負荷が高いリク゚ストごずにHTML生成 動的なコンテンツに適しおいる CSRClient-Side Rendering CSRは、ブラりザ䞊でJavaScriptを実行しおコンテンツを生成する手法です。初期HTMLは最小限で、デヌタ取埗ず描画はクラむアントサむドブラりザで行われたす。 "use client";import { useEffect, useState } from 'react';export default function CSRPage() { const [data, setData] = useState(null); useEffect(() => { fetchData().then(setData); }, []); if (!data) return <div>読み蟌み䞭...</div>; return <div>{data.content}</div>;} " use client " ; import { useEffect , useState } from ' react ' ; export default function CSRPage () { const [ data , setData ] = useState ( null ) ; useEffect ( () => { fetchData () . then ( setData ) ; }, []) ; if ( ! data ) return < div > 読み蟌み䞭 ...</ div >; return < div > { data. content } </ div >; } CSRの特城 初期衚瀺が遅いJavaScriptの読み蟌み・実行が必芁 SEOに䞍利初期HTMLにコンテンツが含たれない むンタラクティブな操䜜が高速 サヌバヌ負荷が軜い SSGStatic Site Generation SSGは、ビルド時に静的なHTMLファむルを事前生成する手法です。 // 静的生成export default async function SSGPage() { const data = await fetchStaticData(); return <div>{data.content}</div>;}// 動的ルヌトでの静的生成export async function generateStaticParams() { const posts = await getPosts(); return posts.map((post) => ({ slug: post.slug }));} // 静的生成 export default async function SSGPage () { const data = await fetchStaticData () ; return < div > { data. content } </ div >; } // 動的ルヌトでの静的生成 export async function generateStaticParams () { const posts = await getPosts () ; return posts . map ( ( post ) => ( { slug : post . slug } )) ; } SSGの特城 最高の衚瀺速床事前生成されたHTML SEOに最適静的コンテンツ サヌバヌ負荷が最小静的ファむル配信 曎新頻床の䜎いコンテンツに適しおいる ISRIncremental Static Regeneration ISRは、SSGの利点を保ちながら、指定した間隔、たたは、任意のタむミングでコンテンツを再生成する手法です。 export default async function ISRPage() { const data = await fetchData(); return <div>{data.content}</div>;}// 60秒ごずに再生成export const revalidate = 60; export default async function ISRPage () { const data = await fetchData () ; return < div > { data. content } </ div >; } // 60秒ごずに再生成 export const revalidate = 60 ; ISRの特城 高速な衚瀺速床静的ファむルを配信 SEOに有利静的コンテンツ サヌバヌ負荷が軜い定期的な再生成のみ 動的コンテンツず静的配信の䞡立が可胜 手法の比范 手法 初期衚瀺 SEO サヌバヌ負荷 適甚堎面 CSR 遅い 䞍利 軜い SPA、管理画面 SSR 高速 有利 重い 動的コンテンツ SSG 最高速 最適 最軜 ブログ、LP ISR 高速 有利 軜い ECサむト、ニュヌス SSR Streamingの解説 さお、ここたでで、各レンダリング手法の仕組みや、メリット・デメリットがお分かりいただけたかず思いたす。 私が担圓しおいるプロゞェクトのゞョブサヌチでは、これたで単にSSRを䜿甚しおいたした。 それは、動的なコンテンツであり、か぀、SEOが倧事なサむトだからです。 しかし、今回お話ししおいるSSR Streamingでは、段階的に衚瀺をさせながら、怜玢゚ンゞンにコンテンツを読み取らせるこずができたす。 HTTPストリヌミング SSR Streamingを理解するには、HTTPの仕様が鍵ずなりたす。 単なるSSRのHTTPレスポンスでは、サヌバヌは完党なレスポンスを生成しおからクラむアントに送信しおいたした。 // 埓来のHTTPレスポンスHTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 1024<!DOCTYPE html><html>...</html> // 埓来のHTTPレスポンス HTTP / 1.1 200 OK Content - Type : text / html Content - Length : 1024 <! DOCTYPE html > < html > ...</ html > HTTP/1.1では Transfer-Encoding: chunked​ ヘッダヌを䜿甚するこずで、1回のHTTPレスポンスを耇数のチャンクに分割しお送信できたす。 よっお、CSRのようにHTMLの読み蟌みが完了した埌に別の情報を取埗するのではなく、1回のHTTPレスポンス内で遅延させた情報も読み蟌むこずが可胜になりたす // ストリヌミングHTTPレスポンスHTTP/1.1 200 OKContent-Type: text/htmlTransfer-Encoding: chunked// チャンク11A<!DOCTYPE html><html><head>// チャンク22F<title>Page Title</title></head><body>// チャンク315<h1>Hello World</h1>// チャンク4E</body></html>// 終了チャンク0 // ストリヌミングHTTPレスポンス HTTP / 1.1 200 OK Content - Type : text / html Transfer - Encoding : chunked // チャンク1 1 A <! DOCTYPE html > < html >< head > // チャンク2 2 F < title > Page Title </ title > </ head >< body > // チャンク3 15 < h1 > Hello World </ h1 > // チャンク4 E </ body > </ html > // 終了チャンク 0 Next.jsでのStreaming実装 では、これをどうやっお実装したら良いのでしょうか Next.js 13のApp Routerでは、簡単に実装するこずができたす Suspenseを䜿甚するこずで、Suspense内郚のコンポヌネントは描画が完了した埌に配信され、ブラりザは受信したHTMLチャンクを段階的に解析・衚瀺したす。 たた、デヌタが送られお来るたでの間は、fallbackの内容を衚瀺しおおくこずができたす。 import { Suspense } from 'react';export default function StreamingPage() { return ( <div> {/* 即座に送信される郚分 */} <h1>即座に衚瀺される郚分</h1> {/* 非同期で読み蟌たれる郚分 */} <Suspense fallback={<div>読み蟌み䞭...</div>}> <SlowComponent /> </Suspense> {/* 耇数の非同期コンポヌネント */} <Suspense fallback={<div>デヌタ取埗䞭...</div>}> <DataComponent /> </Suspense> </div> );}async function SlowComponent() { await new Promise(resolve => setTimeout(resolve, 2000)); return <div>時間のかかる凊理の結果</div>;}async function DataComponent() { const data = await fetch('https://api.example.com/data'); return <div>{data.content}</div>;} import { Suspense } from ' react ' ; export default function StreamingPage () { return ( < div > { /* 即座に送信される郚分 */ } < h1 > 即座に衚瀺される郚分 </ h1 > { /* 非同期で読み蟌たれる郚分 */ } < Suspense fallback = { <div>読み蟌み䞭 ...</ div > } > < SlowComponent /> </ Suspense > { /* 耇数の非同期コンポヌネント */ } < Suspense fallback = { <div>デヌタ取埗䞭 ...</ div > } > < DataComponent /> </ Suspense > </ div > ) ; } async function SlowComponent () { await new Promise ( resolve => setTimeout ( resolve , 2000 )) ; return < div > 時間のかかる凊理の結果 </ div >; } async function DataComponent () { const data = await fetch ( ' https://api.example.com/data ' ) ; return < div > { data. content } </ div >; } Suspenseで囲んでfallbackで指定するだけで埌からHTMLを曞き換えおくれるのは非垞に䟿利ですね ゞョブサヌチでの実装䟋 ゞョブサヌチでは、最初の2件の求人のみを返し、その䞋の求人や内郚リンクはfallbackにスケルトンを指定しおいたす。 export const SearchPage = async ({ searchParams }) => { // 初期衚瀺するための求人情報を取埗 const initialData = await getInitialSearchResults(searchParams); return ( <div> {/* 即座に衚瀺される初期結果 */} <InitialResultsList data={initialData} /> {/* 远加の求人を非同期で読み蟌み */} <Suspense fallback={<LoadingSkeleton />}> <AdditionalResultsWrapper searchParams={searchParams} /> </Suspense> </div> );}; export const SearchPage = async ({ searchParams }) => { // 初期衚瀺するための求人情報を取埗 const initialData = await getInitialSearchResults ( searchParams ) ; return ( < div > { /* 即座に衚瀺される初期結果 */ } < InitialResultsList data = { initialData } /> { /* 远加の求人を非同期で読み蟌み */ } < Suspense fallback = { <LoadingSkeleton /> } > < AdditionalResultsWrapper searchParams = { searchParams } /> </ Suspense > </ div > ) ; } ; 以䞋の郚分で残りのデヌタを取埗しおいたす。 const AdditionalResultsWrapper = async ({ searchParams }) => { // 時間のかかるデヌタ取埗凊理 const additionalData = await getAdditionalResults(searchParams); const relatedData = await getRelatedContent(searchParams); return ( <> <AdditionalResultsList data={additionalData} /> <Pagination totalCount={additionalData.count} /> <RelatedContent data={relatedData} /> </> );}; const AdditionalResultsWrapper = async ({ searchParams }) => { // 時間のかかるデヌタ取埗凊理 const additionalData = await getAdditionalResults ( searchParams ) ; const relatedData = await getRelatedContent ( searchParams ) ; return ( <> < AdditionalResultsList data = { additionalData } /> < Pagination totalCount = { additionalData. count } /> < RelatedContent data = { relatedData } /> </> ) ; } ; スケルトンには実際のコンテンツず同じような倧きさを持぀ようにしお実装しおいたす。 const LoadingSkeleton = () => { return ( <div> {Array.from({ length: 10 }).map((_, i) => ( <div key={i} className="skeleton-item"> <div className="skeleton-title" /> <div className="skeleton-description" /> </div> ))} </div> );}; const LoadingSkeleton = () => { return ( < div > { Array . from ( { length : 10 } ). map (( _ , i ) => ( < div key = { i } className = " skeleton-item " > < div className = " skeleton-title " /> < div className = " skeleton-description " /> </ div > )) } </ div > ) ; } ; これにより、以䞋の画像のように段階的にペヌゞを衚瀺させるこずができたす。 導入結果 ペヌゞ衚瀺速床 さお、今回の倉曎で、ペヌゞ衚瀺は早くなったのでしょうか 結果は、、、 䜓感めっちゃ早くなりたした が、サヌチコン゜ヌルの平均応答時間に倉化はありたせんでした。 いく぀かの条件で調べた結果、おおよそ1秒以内でHTMLが返っおきおいたした。 䌚瀟名 株匏䌚瀟マむナビ 0.35秒 ゚リア 埌玉県 熊谷垂 0.45秒 フリヌワヌド IT゚ンゞニア 0.98秒 これたでは2~3秒皋床かかっおいたので、だいぶ時間が短瞮されおいたす。 䞀方で、サヌチコン゜ヌルでの平均応答時間は、以前ずしお倧きな倀のたたになっおいたす。 おそらく、HTMLの読み蟌みが終わるタむミングを平均応答時間ずしお衚珟しおいるのではないかず思いたす。 怜玢゚ンゞンでの認識 サヌチコン゜ヌル䞊でURL怜査を芋たずころ、埌から読み蟌たせおいる郚分もHTMLずしお認識されおいるこずがわかりたした。 これに぀いおは、Vercelの方でも蚀及されおいる蚘事がありたした。 参考蚘事 たずめ 今回の倉曎を行ったこずで、ナヌザヌ䜓隓に倧きな向䞊があったず考えおいたす。 怜玢゚ンゞンにコンテンツを認識させながらも、ファヌストビュヌの衚瀺をずにかく早くするこずができたした SEO察策ずしお怜玢゚ンゞンに読み取っおもらうコンテンツを䜜成するためにペヌゞ衚瀺速床が犠牲になっおいるサむトには、SSR Streamingはナヌザヌ䜓隓向䞊に非垞に効果的な斜策だず思いたす。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の2日目の蚘事です。 クラりドむンテグレヌション統括郚 クラりド゚ンゞニアリング郚 ゚ンゞニアリング3課のO.K.です。マむナビに入瀟しお玄1幎半ほど経過し、珟圚は AWS Organizations 呚りの統制を担圓しおいたす。 抂芁 2025幎9月頃から AWS re:Invent の参加に向けお準備を進めおいたした。マむナビずしおの参加はおそらく3回目になりたすが、「初参加 + クラりド゚ンゞニア目線」で準備したこずなどをたずめおみたす。 セッションの参加に向け考慮すべきこずや、事前に準備しおいるこずなどを蚘茉したす。 セッション予玄 事前情報 AWS re:Invent 公匏ペヌゞ セッション数 2564 セッション倚セッション予玄開始時は1500件ほどだった気がしたす ※画像は 11/13 時点のもの 䌚堎党䜓 Encore Las VegasWynn Las Vegas ず最南端の Mandalay Bay Resort and Casino の間で、 çŽ„ 3 マむル ≒ 4.8 km遠 参考 https://markrosscloud.medium.com/how-i-re-invent-2f9f7de0f1bb 思った以䞊に難しいスケゞュヌル登録 芏暡感は分かったのですが、公匏サむトから気になるセッションを怜玢・予玄をしおいくず、䞭々郜合が悪くなっおしたいたす。 䟋えば 気になるセッションの時間垯が重なる 䞭途半端に空いた時間ができる 䌚堎移動を考慮しづらい ずいった具合です。うたいこずフィルタヌを掻甚し、頑匵れば調敎できるのですが、䞭々難しい。 セッションの䞭には早期に予玄しないずすぐに埋たっおしたうものがあるので、早めに暫定スケゞュヌルを組んでしたいたいずころでした。 そんな時に出䌚ったのが AWS re:Invent Planner 2025 ※AWS 公匏のツヌルではないです re:Invent Planner どのようなこずができるか 公匏サむトず同様、巊偎のナビゲヌションバヌ的な箇所でセッションをフィルタリングするこずができるのですが、特に䟿利だったのが時間指定です。 公匏でフィルタできる情報に加え、特定の時間でどういったセッションがあるのか調べるこずができたす。䟋えば以䞋のように蚭定するず Level300 -Advanced TypeBulders'session DayMonday (12/1) Time開始時間8:00 以降、終了時間13:00 前 VenueMondalay Bay 出おくるセッションは以䞋のような感じになりたす。 Venue and Day/TimeMonday @ Mandalay Bay | Level 2 South | Oceanside C | Content Hub | Builders' Session 210:30 - 11:30 Venue and Day / Time Monday @ Mandalay Bay | Level 2 South | Oceanside C | Content Hub | Builders ' Session 2 10 : 30 - 11 : 30 指定通りのセッションになっおいたすね。 この調子で気になるセッションを埋めおいくず  12/1 は䞀぀の䌚堎Mandalay Bay はオレンゞ色でセッションスケゞュヌルを立おるこずができたした。お昌は Mandalay Bay 䌚堎の昌食を楜しみ぀぀、ブヌスを芋お回りながら雰囲気を堪胜できそうです。 ※ 初日は参加メンバヌ党員が同じ䌚堎で行動しおみようず決めおいたした。(午埌の Game Day に党員で参加するため) 事前に準備しおいたため、メンバヌ党員がセッション予玄開始ず同時(10/15 AM2:00)に気になるセッションを予玄できたした セッション参加に向けた準備 参加する䞻なセッション 私が参加しようず思っおいるセッションは倧䜓こんな感じになりたした。 Kiro AWS Bedrock, Amazon Q Developer の掻甚開発運甚、むンシデント分析 モダンな CI/CDゲヌム圢匏 8, 9割は AI が関連するセッションずいった感じです。個人的にも意識しお遞びたしたが、党䜓的に AI 関連のセッションがかなり倚いです。 恥ずかしながら、私はただただ AI を掻甚した経隓が少なく、Amazon Q Developer や Bedrock を掻かしきれおいないず思っおいたので、このようなセッションを重点的に孊がうず思いたした。 Kiro 数か月前から話題になっおいる AWS が開発した統合開発環境(IDE)です。VScode に䌌た UI で、AIが暙準で統合されおいたす。Cursor ず䌌おいたすね 個人的には仕様駆動型開発を前提に䜜られおいるのがめちゃくちゃ奜きになりたしお、プラむベヌトではこの゚ディタに移行しようず思っおいたす。 仕様駆動型 ドキュメントベヌスで「䜕を䜜るのか」を明確にし、出来䞊がった芁件、蚭蚈に埓っお AI が機械的にタスクを実行しおくれたす。 これがかなり良さそうだず思っおいたす。䟋えば Kiro に䌝えるための「AWS のむンフラ構築に関するテンプレヌト」を瀟内で甚意 テンプレヌトを枡しお  requirements.md ​を䜜成 蚭蚈ずなる  design.md ​ ã‚’䜜成2⇔3 でいい感じに調敎 tasks.md ​ ã®ã‚¿ã‚¹ã‚¯å®Ÿè¡Œ みたいな感じで行けそうかなず。Kiro は AWS が開発しおたすし、芪和性はかなり高い はず https://kiro.dev/ AWS Bedrock Bedrock に登録されおいる汎甚的なモデルを API で呌び出すずいった圢で䜿甚するこずはありたしたが、 RAG 構成を実装したりしたこずはなかったため勉匷しおおくこずにしたした。䞋蚘の曞籍を読み進める䞭で参考になる情報はたくさんありたしたが、AWS で構築する RAG 構成を簡単に玹介したす。 参考 Amazon Bedrock 生成AIアプリ開発入門  â€»ã‚ã¡ã‚ƒãã¡ã‚ƒã‚ªã‚¹ã‚¹ãƒ¡ Knowledge bases (RAG) 现かい説明は割愛したすが、芁するに「瀟内文曞などの情報を LLM に䞎えお回答させる」ずいった構成の䞀぀です。 具䜓的な凊理の流れずしおは以䞋のような感じです。 S3 から瀟内文曞を読み蟌み、埋め蟌みモデルがベクトルに倉換しおベクトルDBに保存 ナヌザヌの質問を埋め蟌みモデルがベクトルに倉換、ベクトルDBの䞭で近い内容ベクトルの類䌌性を評䟡があるか怜玢 怜玢結果を LLM に枡す LLM がナヌザヌにレスポンスを返す 「现かいこずはよく分からない」ずいった堎合にも、緑色の枠の郚分を1぀のLLMずしお考えるこずで、シンプルにS3から瀟内文曞を参照しお回答しおいるように芋えたす。 たずめ 今回は初参加目線のセッション遞びず、その事前準備に぀いおたずめおみたした。その他にも、参加するセッション (Game Day) のモダンな CI/CD 構成に぀いお事前予想を立おおみたり、ツアヌ䌚瀟ずのやり取りや瀟内調敎、さらには英語を勉匷し盎したり等々  色々曞きたいこずはあるのですが、倧分長くなりそうなので以䞊にしようかなず思いたす。 むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
本蚘事は【 Advent Calendar 2025 】の1日目の蚘事です。 はじめに 盎近数幎くらいで、プロトタむピングの手法がかなり発達しおきたなず感じおいたす。 数幎前に、FigmaやAdobe XDずいったデザむンツヌルが登堎し、 Figmaのプロトタむプ機胜を䜿えば、デザむンツヌル䞊でむンタラクションたで䜜れる ようになりたした。 そしお最近、AIを掻甚したプロトタむピングがかなり進化しおきおいたす。 個人的に思うのは、 「今たで数日や数時間かかったプロトタむピングが䞀瞬で終わる時代になっおきた」 ずいうこずです。 最初から完璧なコヌドを曞くよりも、プロトタむプをサクッず䜜っお䟡倀怜蚌しおいく方が圧倒的に楜だし、いろんなアむデアを詊せるんじゃないかなず思っおいたす。 今回は、そんなAIを䜿った新しいプロトタむピング手法に぀いお、 Figma Make を題材にたずめおいきたす。 新旧のプロトタピングを比范する題材 新旧のスピヌド感や劎力を比范しおいくので、題材を合わせおみたす。 今回は、 「名蚀をスワむプしおみれるアプリ」 を題材にやっおみようず思いたす。 埓来のプロトタむピング手法Figma prototype たずは埓来のパタヌンで䜜っおいきたす。 䜜成颚景も茉せおみたので、なんずなくこんな感じにで䜜っおいるんだなず思っおみおみおください。(100倍速) ① 芁玠を䜜る たずは、枠を䜜りたす。これは普通のFigmaファむルを䜜るの倧䜓同じです。 ② バリ゚ヌションを䜜る 今回は5぀の名蚀があるので、5パタヌン䜜りたす。 ③ ナビゲヌションで繋げる ナビゲヌションで5画面を繋げたす。 スワむプみたいな䟿利おそらくないは、䜿わずクリックむベントで擬䌌的に䜜っおたす。 むンタラクションは党郚同じです。 ちなみに、 Animation: Smart animate を䜿うずいい感じにアニメヌションしおくれるので倧倉䟿利です。 かかった時間ず劎力 動画の時間が、 倧䜓17分 くらいでした。初めおの人はもう少しかかるず思いたす。 䜜業の劎力ずしおは、慣れおればそんなに蟛くはないのですが、画面にはずっず匵り付きっぱなしになるず思いたす。 新しいプロトタむピング手法Figma Make (AI) を䜿う ここからが本題です。 AIを䜿った新しいプロトタむピング手法に぀いお玹介しおいきたす。 0→1れロから䜜るでも䜿えるし、既存のFigmaファむル がある状態から修正しおいくこずもできたす。 今回はFigma Make AIを䜿いたすが、V0やLovableでも䌌たようなこずはできたす。 ただ、今回は、Figma Make AIに絞っお話を進めおいきたす。 0→1 パタヌン たず、䜕もない状態から䜜る堎合の流れです。 ①AIず壁打ちをする 最初にやるのは、 ClaudeやChatGPTず壁打ち です。 Figma Make AIに投げる前に、たず芁件を敎理しおおくずスムヌズに進みたす。 欲しいもの 芁件定矩 仕様の郚分 倧䜓の画面䞀芧 この3぀があれば、AIは割ずいい感じに䜜っおくれたす。 ちなみに、音声入力で2〜3分くらい、だらだらず喋るのがおすすめです。 「こういうアプリを䜜りたいんですよね。ナヌザヌは〜で、機胜ずしおは〜があっお、画面は〜ず〜が必芁で・・・」 みたいな感じで、思い぀いたこずを党郚喋っちゃいたす。 情報量は倚い方がいいので、遠慮せずに党郚話したしょう。 ※䌚瀟では、claudeは䜿甚できないので、䜙暇で実斜しおいたす。 ②AIに敎理しおもらう 壁打ちした内容をAIに投げたら、次は敎理しおもらいたす。 【プロンプト䟋】 今話した内容を敎理しお、足りない郚分確認が必芁な事項をたずめおください。その埌、画面定矩ず芁件定矩を生成しおください。 こんな感じで投げるず、AIがいい感じに敎理しおくれたす。 足りない郚分があれば質問しおくれるので、それに答えながら詰めおいきたす。 ③Figma Makeに流し蟌む 芁件が固たったら、いよいよFigma Make AIに投げたす。 ここはもう本圓にシンプルで、 【プロンプト䟋】 Figma Make AIに流したいので、芁件定矩曞ずドキュメントを䜜っおください。 これだけです。 するずAIが、Figma Makeに投げやすい圢匏で敎理しおくれたす。 ↓出力されたマヌクダりン そのたたコピペでFigma Makeに投げればOKです。 ④チャットで調敎 Figma Makeがプロトタむプを生成しおくれたら、あずはチャットで調敎しおいくだけです。 「このボタンの色を倉えお」 「この画面に〜の芁玠を远加しお」 「レむアりトをもう少し広くしお」 みたいな感じで、察話的に修正できたす。 今回は背景だけ倜空に倉えおもらいたした。 ↓こんな感じになりたした。 かかった時間ず劎力 動画の時間が、 倧䜓10分 くらいでした。 時間も早かったですし、 アニメヌションの融通が利きやすい (スワむプ可胜) AI埅ちの時間が倚いので、ながらで進めやすい 画面を耇補する手間がない (耇補すればするほど现かい調敎が倧倉になっおくる) あたりが良かったなずいう印象ですね。 やはりプロトタむピングは、Figma Make 系のツヌルで良いのではず思っおしたいたした。 既存のUIがあるケヌス 既存のUIもうすでにFigmaにあるケヌスで、それのプロトタむプをしたい堎合も、 FigmaMakeが匷いです。 Lovableやboltなどは、Figmaの連携機胜がなかったり、乏しかったりしたす。 v0は課金プランで利甚可胜 やり方は簡単でこな感じで、Figmaから、 Send to > Figma Make ずするだけです。 送信するず、テキストボックスに 該圓のFigmaが入っおいくので、プロンプトを入力しおみたす。 今回は、雑にスクロヌルのアニメヌションを改善するずいう課題にしたした。 この “雑に“ 投げられるずいうのが、かなりポむントです。 「このUIどうしようかな・・・」ず思った時にずりあえず、「FigmaMakeに投げおみお、発散しおみよう」みたいなこずができたす。 ちなみにできのたがこんな感じです。リッチなアニメヌションになりたした。 どんなツヌルを䜿うべきか・・・? 色々なツヌルがある䞭、今回は、Figma Make を解説しおきたした。 個人的には、AIでアプリケヌションの粟床ずしおは、あたり差はないように思えたす・・・。 そのため、呚蟺機胜などで、利甚するものをかけるこずが倚いですね。 個人的な䜿い分け方 個人差ありたすが、自分はこんな感じで䜿い分けおいたす。 ケヌス① Figmaずの芪和性 既存Figmaがある、最終的にFigmaファむルに萜ずしたい → Figmaのむンポヌト゚クスポヌトができるFigmaMake ケヌス② DBやGitHubずの連携 個人開発くらいの堎合DBたで連携しおリリヌスを想定 → SupabaseやGitHub連携がしやすいv0・Lovable ケヌス③ モバむルアプリ モバむルアプリを䜜りたい → ReactNative が曞き出せるbolt → flutter が曞き出せるFlutterFlow FigmaMakeにずらわれずに、いろんなツヌルを觊っおみお、良し悪し研究しおいきたいですね。 たずめ 最埌たで芋おいただいおありがずうございたした。 AIの粟床もここ䞀幎くらいで、䞊がっおきたので、来幎はどのくらいになっおいるのでしょうか・・・ 正盎想像もしたくないくらい怖いですが。 なるべく、この波に乗るべく、色々なツヌルを䜿っおいきたいずころですね・・・ むベント告知 12月23日にむベントを開催したす申し蟌みはこちらから▌ https://mynaviit.connpass.com/event/376769
Advent Calendarアドベント カレンダヌに぀いお Advent Calendarは、クリスマス12/112/25たでの日数をカりントダりンするカレンダヌのこずですが、IT業界では「技術蚘事を日替わりで公開する䌁画」ずしお定着しおいたす。 そこで、マむナビ゚ンゞニアブログでも、明日からクリスマスたでの期間、有志の゚ンゞニアやマネヌゞャヌが参加し、ITに関する蚘事を毎日1本ず぀投皿しおいきたす。 ぜひご芧ください 蚘事䞀芧 蚘事公開埌、順次曎新予定ずなっおおりたす。 日付 蚘事 12/1月 Figma Make (AI) を䜿った新しいプロトタむピング手法 12/2火 Coming Soon 12/3氎 Coming Soon 12/4朚 Coming Soon 12/5金 Coming Soon 12/8月 Coming Soon 12/9火 Coming Soon 12/10氎 Coming Soon 12/11朚 Coming Soon 12/12金 Coming Soon 12/15月 Coming Soon 12/16火 Coming Soon 12/17氎 Coming Soon 12/18朚 Coming Soon 12/19金 Coming Soon 12/22月 Coming Soon 12/23火 Coming Soon 12/24氎 Coming Soon 12/25朚 Coming Soon