TECH PLAY

株式会社Insight Edge

株式会社Insight Edge の技術ブログ

166

はじめに なぜ展示会に出たのか 準備1:どこにだすか?誰とだすか? 準備2:デモ・営業資料・リーフレット...終わらない制作物 会場設営、そして当日 終わりに:展示会はチームでつくるもの はじめに こんにちは、Insight Edgeイノベーションハブで事業開発を担当している那須田です。 2025年5月に、大阪で開催された「 コンタクトセンター/CRMデモ&カンファレンス 」(通称CCCRM展)という展示会に参加してきました。半期ごとに行われているイベントで、コンタクトセンター関係者が多く来場し、先端技術を活用したツールやソリューションの展示が行われています。昨年から参加し始めた当社としては3回目の出展となります。 多くの方は「見る側」として展示会参加経験があるのではないかと思いますが、「出す側」として構想から当日のお客様あて説明・後片付けまで関わる機会は意外と少ないのではないでしょうか。そこで、今回は「展示会って実際どうやって準備してどうやって終わっていくの?」という疑問にお答えすべく、体験談をまとめたいと思います。 なぜ展示会に出たのか Insight Edgeでは住友商事および住友商事Gr企業の多業界・グローバルに広がる広いフィールドを中心に、POC〜システム開発をワンストップで素早く行うことでDX実装を行っています。 各顧客の悩みに寄り添いながら個別プロジェクトを進めていくことが多いのですが、特に生成AI登場以降は、テーマやアウトプットが「これはいろんなお客様に使っていただけるのでは?」というものが増えており、ソリューションとして広く展開する取り組みも行っています。そのうちの一つが顧客の声・従業員の声分析を大胆に効率化・高度化出来るテキスト分析ツール「 Voiceek(ボイシーク) 」です。こちらのツールは顧客にご利用いただく中や導入にむけたPOCを進める中で出てきた要望や気付きをもとに継続して成長を続けています。 生成AI活用したVOC分析ツール「Voiceek」  また、他にもコンタクトセンター関連での取り組み・知見がたまってきています。顧客あての営業電話の架電優先順位の最適化するAIモデル(「架電最適化モデル」)や、将来かかってくるコール数の予測に基づいた必要な応対オペレータ数予測を行うAIモデル(「オペレータ数予測モデル」)の取り組みが出てきており、ソリューション化を検討しています。 展示会への出展目的は、出展物のいるステージによってさまざまです。一般的には以下あたりがあるのではないでしょうか? 見込み顧客獲得:名刺交換などを通じて営業フォローするための情報を集める 認知向上・ブランディング:新サービス発表タイミングでの露出強化 既存顧客との関係強化:対面でのさらなるリレーション構築・アップセルの機会 パートナー・アライアンス開拓:協業候補企業との出会いの場 仮説検証:顧客とのナマの会話でニーズを理解したい 今回の出展では、もちろん新たな顧客獲得は狙いたいので大目標としているのですが、出展物それぞれに裏テーマがありました。Voiceekについては「さらなる進化に向けたニーズの確認」というテーマ。「架電最適化モデル」や「オペレータ数予測モデル」についてはそもそものニーズ確認というのが重要なテーマでした。個別プロジェクトで実施した取り組みにそもそも広いニーズが有るのか?というところです。 準備1:どこにだすか?誰とだすか? まずは、出展の目的に応じて展示会を選ばないといけません。なんの切り口で探しに行くかというのは意外と難しいところだと思います。例えば「AIエージェント」関連でみてみると、少し調べただけでも片手に収まらない数が出てきます。その中から、出展のためのコストやそこで出会える顧客層、商談に実際に進んでいく本気度があるかなどで絞り込んでいく必要があります。 ChatGPTで検索したAIエージェント関連のイベント こういった問題を一気に乗り越えられる方法が「出展パートナーを見つける」ということです。冒頭で書いた通り、当社のCCCRM展への参加は3回目となるのですが初回から継続してSCSK・SCSKサービスウェアとの共同出展です。 初回参加のきっかけとしては、Voiceekのベースとなった取り組みでのアプリケーションをSCSK宛に紹介し、SCSKがコンタクトセンター向けに提供するツール群とのシナジーがあることも見えたことでCCCRM展への共同出展のお誘いをいただいたという形でした。長年同イベントに参加しているSCSKから、イベント来訪者層や準備にどの程度の負荷がかかるか当日の動き方などのレクチャーを受け、多くの人員を割けるわけではない我々でも参加できそうかつ、反応を見たい顧客層が来訪すると確信して参加を決定することができました。 また、出展するブースの位置も非常に重要で、これによって来場者自体は多くても場所が悪い自社ブースはすっからかん...ということもありえます。イベントにもよると思いますが、ここも長年参加しているSCSKだからこそ把握しているスイートスポットの確保ができており、成功のために重要なポイントです。実際、イベント当日フロアを回ってみると、あの有名な企業が場所のせいで閑古鳥...というところもあり、体力をかけて準備した全てが無駄になりかねません。 やはり事情に通じている先輩と一緒に出すというのは特にビギナーにとっては非常に大事なポイントな気がします。 準備2:デモ・営業資料・リーフレット...終わらない制作物 さて、どこに出すか・誰と出すかが決まればあとはつくるのみ!です。上で触れた通りSCSK・SCSKサービスウェアとの共同出展ですので、3社での制作物と自社での制作物があります。濃淡ありますが当社が関わったものは以下があります。 3社制作物 既存顧客あてにイベント周知するための「事前チラシ」 当日、来訪客あてに配るための「当日チラシ」 当日、ブースにはる大きなパネル 単独での作成 営業資料:すでにあるものをアップデートや新規作成 チラシ:3社のものとは別に「Voiceek」宣伝用 Voiceekデモ環境:顧客層に合わせたサンプルデータの作成とデータ適用した環境準備 Voiceekベータ機能:顧客のニーズ・反応を確認したい機能の実装 ミニセミナー資料: 各出展物の紹介資料を10分以下のトーク用に整理 トークスクリプト 紹介動画:ミニセミナーで活用するVoiceek機能紹介動画 準備は2〜3ヶ月前から始め、週次定例で相談をしながら進めていきました。恥ずかしながら、、前半はなかなかエンジンが掛かりません。まだ何をどう作るか見えていないというのもあって作業量が具体的に見えてないせいもあるかもしれません。 本格的に焦り始めるのは残り1.5ヶ月というあたりからです。作るべきものが見え、あまりの多さに圧倒されます(自業自得です...)。ここからはなんとか作成を間に合わせるため(周りのメンバーに迷惑をかけながら...)一気にギアを変えて取り組みます。たくさんの制作物があるため、単独で対応することはまず難しいでしょう。各プロジェクトのメンバーを巻き込んでヒアリング・場合によっては制作を手伝って貰う必要もありますし、お客様の目を引くためにはデザインにもこだわる必要があります。 幸い当社は部署ごとの垣根が低く協力を仰ぎやすく、また社内に強力なデザインチームがあり目線合わせをしながらクイックに進められたことでなんとか締切に間に合わせることができました。逆に、社内が縦割りの気配あり...だったり、デザインの外注対応を前提とされている場合は、コミュニケーションのリードタイムや意思疎通の齟齬を取り戻す時間をしっかりと見ておかないと「締切に間に合わない!」という事がありえます。準備期間の落とし穴の一つとして知っておくべきことかもしれません。 これ以外にも、ロジ面での調整が多数存在します。wifiはつかう?ブースの造作はどうする?組み立ての業者は?当日使うPCは?当日の集客のためのチラシ配り要員は?...地味ですが決めることは山積みです。ロジ面は、表に出る出展物にも増して経験がモノを言う分野になります。先程の繰り返しとなりますが、やはり出展経験があるパートナーとの出展がおすすめです。 会場設営、そして当日 前日 なんとか制作物を間に合わせた、と一息つこうとするとすぐにイベントです。CCCRM展では、イベント前日の午後から会場設営が始まります。作成したパネルやその他ブース設営は展示会対応を行う業者がおり、対応してもらいます(ロジとして固めておく)。周囲のブースの設営を観察していると、どこまで業者に対応してもらうかは各社ごとに異なります。準備要員が足りなくてかなり苦労している会社も見受けられました。 顧客宛説明をするためのPC・モニタはレンタルすることが多いと思います。それらの設定は自分たちで行います。何らかのソフトウェアのインストールが必要な場合は十分なスペックを持ったPCかどうかは意識しておいたほうが良いでしょう。 前日準備の様子:ブースに設置したパネル 当日 当日が来ました。イベントスタート前に朝礼をして、名刺交換のルールやもらったコメントのメモの仕方など目線合わせをしておきます。これをしないと対応者によって得られる情報にばらつきが出て、顧客ニーズの理解が進まなかったり、場合によっては潜在顧客を気づかずに逃してしまうということになります。Insight Edgeチームでは、事前にニーズを探るための観点と質問をリストアップしておいて手元に持っておくようにしておきました。 10時になり開場します。ここからは立ちっぱなし、喋りっぱなしです。イベントにもよると思いますが、CCCRM展は多くの来訪者があり、座る時間というのはほぼありません。そのため、足腰と喉の耐久ゲームとなります。ただ、応対すればするほどだんだん自分の対応がこなれてくるということに気づき、ある種の「展示会説明者ハイ」のようなものが訪れます。とはいえ、、状況が許せばシフトに少し余裕を持てる人数を用意しておくことをおすすめします。 忙しい中でも一応繁閑は発生します。お昼時やセミナーが開催されている時間等が少し客足が落ち着くタイミングとなります。このあたりもイベントごとに違うと思うので事前に見極めてシフト組みに活かせるとスムーズです。 また、来訪者の傾向として、各社複数名でイベントにいらっしゃる印象です。そして、なにかツール選定などテーマを持って来場している場合、全体を各人がグルっと回った上で良かったブースを意見交換。その後、全員(3名など)で改めて良かったブースを来訪という形で進めているように見えます。当たり前のことを書いてしまいますが、毎回「一球入魂」の説明をしないと二回目の訪問をもらえず予選落ちしてしまうということになるので、特に疲れが溜まってくる午後の説明で気をつけるべきポイントです。 CCCRM展は2日間のイベントですが、展示会ハイのうちにあっという間に終りを迎えます。展示会が終わりには夕礼が開催されます。ここで集客の結果が発表されます。各ブース悲喜こもごもの様子が見られ、アツくなります。結果によって打ち上げの盛り上がりが左右されるでしょう。 さて、最後は後片付けです。PCのレンタル元への返送や制作物の処理などがあります。会場の平常時間もあるのでしっかりロジを確認しておきましょう。また、制作物については今後の営業活動や次回以降の展示会で使えるものもあるのではないでしょうか?特にチラシについてはイベント以外でも気軽に使えるものかつ印刷コストも大きくないので今後の活用のために多めに準備して余らせても良いかもしれません。 当日の様子:Voiceekミニセミナー実施中 終わりに:展示会はチームでつくるもの 最後に、上で触れなかったが重要と思っている展示会参加の価値について改めて振り返っておきたいと思います。 締め切り効果 ツールでもソリューションでも立ち上げ期は明確な締め切りが作りにくい部分があると思いますが、展示会はどうあがいても動かせない締切を作ってくれます。そして、締切後にそのまま使える資料や機能など様々なレガシーを残してくれます。これは見逃せない大事なポイントだと思います。 チームとして動く効果 チームでカバーし合いながら動くことでしか成功裏にイベントを終えることはできません。というか、そもそもイベントにたどり着けません。また、「締切に向けて制作物を決めてそれをクリアしていく」という小さくはあっても成功体験をチームで積めることでその後のチーム内での連携の形の原型も作られます。立ち上げ期にあるチームの形作りの場としてや、逆に営業サイドと開発サイドが分離されてしまっている状態からの再度のチームアップのきっかけとしても機能すると思います。     以上、地味な側面も含めて、展示会出展にあたって先に知っておきたかったなと思うポイント・参加の価値を振り返ってみました。 Insight Edgeでは、MVVのValueとして「やりぬく」、「やってみる」、「みんなでやる」を掲げて、クライアントワークでなく事業会社のデジタルバリューアップ実現を担うパートナーとして事業を行っています。展示会は参加のお誘いを受ける形で「やってみる」で始め、小さなサークルでは対応しきれないことに気づき、様々な社内外メンバーを巻き込んで「みんなでやる」イベントとして盛り上げ、締切までに「やりきる」というまさに当社Valueを総動員するようなイベントでした。Valueを体現して助けていただいた皆様に大変感謝しています! 今後も当社は新しい領域のソリューション開発を進めていきたいと思っています。事業開発ポジションも鋭意募集中ですので、是非下のバナーからご確認の上ご応募ください!
アバター
こんにちは。データサイエンティストの白井です。 今日は、LLMのEmbeddingをアイテム推薦に活用すると、どんな推薦が可能になるかを紹介したいと思います。 はじめに アイテム推薦とは アイテム推薦の種類 行動履歴ベース推薦 コンテンツベース推薦 LLMのEmbeddingはコンテンツベースの強い味方になる 利用データについて MovieLens TMDb 行動履歴ベース推薦の実践 実施内容 推薦結果 分かること 1. 「ハリーポッターと賢者の石」の結果が、シリーズが並んでいる。 2. 「もののけ姫」や「君の名は」の結果が、同じ制作会社や監督が並んでいる。 3. 全体的に、推薦対象映画の上映年と近い年代の映画が並んでいる。 LLMのEmbeddingを用いた推薦の実践 実施内容 推薦結果 分かること 1. 「ターミネーター」と「ハリーポッター」の結果が、シリーズで完全に埋まっている。 2. 「もののけ姫」と「君の名は」はの結果が、同じ制作会社や監督の作品に偏っていない。 3. 全体的に、推薦映画の上映年の偏りが見られず、満遍なく推薦されている。 鬼滅の刃 無限列車編と推薦結果のあらすじ 未来の映画の推薦 2025年7月公開の映画のあらすじの作成 推薦結果 終わりに 補足 Item2Vecのハイパーパラメーター はじめに アイテム推薦とは アイテム推薦とは、「あなたにおすすめの商品はこれ!」といったかたちで、ユーザーの興味・関心に合った商品を提示する機能です。 この機能は、YoutubeやAmazon、Uber Eatsなど様々なサービスで提供され、膨大な選択肢の中から“自分好み”を次々と提案してくれます。 アイテム推薦の種類 アイテム推薦は、大きく分けて2つに分類できます。 行動履歴ベース コンテンツベース 行動履歴ベース推薦 行動履歴ベース推薦は、ユーザーの行動履歴を手がかりに、似た行動パターンを持つ人が好んだアイテムを推薦する方法です。 ユーザーの行動履歴データがあれば、それと一緒にアクションされているアイテムが推薦できます。 一方で、まだ誰も触れたことがない、新規アイテムやニッチなアイテムを推薦できない課題があります。 コンテンツベース推薦 コンテンツベース推薦は、アイテムのジャンルなどの属性や、商品の説明文などでマッチするアイテムを推薦する方法です。 属性データや説明文のデータがあれば、新規アイテムも推薦できます。 一方で、属性データの設計や設定にコストが高い点と、属性データなどで表現しきれない嗜好を捉えにくいという課題があります。 LLMのEmbeddingはコンテンツベースの強い味方になる コンテンツベース推薦における文章データには、これまで主にTF-IDFなどが用いられてきました。 これに対し、近年流行しているLLM(大規模言語モデル)のEmbedding機能は、文章をベクトルで扱うことが可能です。 LLMのEmbeddingを利用することで、アイテムの説明文やレビューなどの自然言語をそのままベクトル化し、テキストの潜在的意味を加味した柔軟で表現力の高い類似度計算が可能になると考えられます。 本記事では、映画のレビューデータと映画のあらすじデータを用いて、行動履歴ベースとLLMのEmbeddingを用いたコンテンツベース推薦を試してみます。 そして、この2つの推薦結果から、それぞれの傾向を確認してみます。 利用データについて 今回利用するのは、 MovieLens と TMDb です。 それぞれ簡単に紹介します。 MovieLens MovieLensは、ミネソタ大学のGroupLens Researchによって公開されている映画のレーティングのデータセットです。 推薦システムの研究では定番のデータセットで、様々な論文で扱われています。 誰がいつ何の映画をどう評価した(1~5の0.5刻み)というレーティングデータや映画のタグ(コメディ・アクションなどのジャンル等)のデータがあります。 データセットの種類がいくつかあるのですが、今回は最近の映画も確認したいため、ml-latestを利用しました。(2025/6/1現在) TMDb TMDbは、映画やテレビ番組に関する情報を取得できるAPIを提供しているサービスです。 映画のあらすじやタイトルの受賞の有無、主演者など、リッチなデータが取得できます。 MovieLensのmovie_idと紐付けることができるようになっており、これを使って映画のあらすじを取得しました。 ※ 本記事では念の為、TMDbのサポートフォームから記事の内容を伝え、記事での利用に許諾をいただきました。 行動履歴ベース推薦の実践 実施内容 今回は行動履歴ベースの手法の中で、比較的簡易に利用でき、かつ強力なItem2Vecを用います。 データの前処理として、レビュー件数が多い上位5%のユーザーのデータを除外しました。 レビュー件数が多いユーザーは、映画が非常に好きなヘビーユーザーと考えられ、ノイジーな動きになっていることが想定されるためです。 また、レビューの点数は、評価が1.0〜5.0の0.5刻みとなっていますが、4.0以上のデータだけを学習に利用しました。 好みが近い映画を推薦したいため、4.0以上を高評価と判断してこのデータだけで学習することで、好まれる組み合わせを発見することが狙いです。 本記事の最後に、利用したハイパーパラメータを記載しておきます。 以下は、Item2Vecを用いた推薦の全体像です。 Item2Vec推薦の概要 推薦結果 上記で学習したモデルを用いて、推薦される映画を確認します。 今回は完全に私の好みで、以下の映画に対する推薦結果を確認します。 ターミネーター2 ハリーポッターと賢者の石 もののけ姫 劇場版「鬼滅の刃」無限列車編 君の名は マッドマックス怒りのデス・ロード 上記の映画に対する、Item2Vecでの推薦結果の上位5件は以下でした。 ()内の数字は上映年です。 Item2Vec推薦結果 分かること 以下3点が目につきました。 「ハリーポッターと賢者の石」の結果が、シリーズが並んでいる。 「もののけ姫」や「君の名は」の結果が、同じ制作会社や監督が並んでいる。 全体的に、推薦対象映画の上映年と近い年代の映画が並んでいる。 1. 「ハリーポッターと賢者の石」の結果が、シリーズが並んでいる。 「ハリーポッターと賢者の石」の推薦結果は、シリーズの2,3,4という順でおすすめとなりました。 シリーズの1を見て面白かったら、シリーズ2,3と連続して鑑賞することや、作品の雰囲気が似ているため高評価が続くのは、想像しやすいかと思います。 行動履歴ベース推薦では、高評価の時系列を考慮するため、このような結果が見られるのかなと思いました。 2. 「もののけ姫」や「君の名は」の結果が、同じ制作会社や監督が並んでいる。 「もののけ姫」はジブリ作品が並び、「君の名は」は新海誠作品が1位でおすすめされています。 これも「ハリーポッター」同様、ある作品を見て面白ければ、同じ監督や制作会社の作品を見てみるという行為から、このような結果が得られていると推測できます。 3. 全体的に、推薦対象映画の上映年と近い年代の映画が並んでいる。 例えば、「ターミネーター2」は1990年前後の上映年の映画がおすすめされており、「君の名は」は2010年代後半がおすすめされています。 これも、行動履歴ベースの学習が、レビューの前後関係を用いているところに影響されているのかなと思っており、同年代の作品は連続してレビューがされやすいためかと想定されます。 全体的に、有名な映画が並んでおり、納得感が高い結果だと思いました。 LLMのEmbeddingを用いた推薦の実践 実施内容 Embedding用のモデルにはOpenAIのtext-embedding-3-smallを用いました。 Embedding用のモデルは様々ありますが、text-embedding-3-smallは登場から時間は経過しているものの精度は悪くなく軽量であるため、採用しています。 これを用い、映画のあらすじデータをEmbeddingし、chromaDBでcollectionを作ってqueryで類似テキストを持つ映画を検索しました。 以下は、LLMのEmbeddingを用いた推薦の全体像です。 Embedding推薦の概要 推薦結果 LLM Embeddingでの推薦結果の上位5件は以下でした。 Embedding推薦結果 分かること Item2Vecの結果と対比して、以下3点が目につきました。 「ターミネーター」と「ハリーポッター」の結果が、シリーズで完全に埋まっている。 「もののけ姫」と「君の名は」の結果が、同じ制作会社や監督の作品に偏っていない。 全体的に、推薦映画の上映年の偏りが見られず、満遍なく推薦されている。 1. 「ターミネーター」と「ハリーポッター」の結果が、シリーズで完全に埋まっている。 どちらもTOP5が全て、シリーズもので埋まっています。 どちらも、"ターミネーター"や"ハリーポッター"などの固有名詞があらすじ内で現れやすく、これがEmbeddingしたベクトルに強めに現れて、cos類似度が高くなりやすいのではと想像しました。 2. 「もののけ姫」と「君の名は」はの結果が、同じ制作会社や監督の作品に偏っていない。 Item2Vecの結果では、同じ制作会社や作者の映画が多く見られましたが、Embeddingの結果では、「もののけ姫」の結果に「ゲド戦記」が1つあるだけでした。 これは、そもそもあらすじには、制作会社や作者を表す単語が出現しないためだと考えられます。 具体的には、"ジブリ"や"新海誠"などの単語があらすじに出現せず、Embeddingベクトルに表現されないため、これで類似度を計算しても"ジブリ"や"新海誠"要素が考慮されないと想像しています。 3. 全体的に、推薦映画の上映年の偏りが見られず、満遍なく推薦されている。 Item2Vecでは全体的に推薦対象の映画と、推薦結果の映画の上映年が近いことが特徴的でしたが、Embeddingでは偏りが見られませんでした。 Embeddingでは、レビューの時系列などを一切考慮しないため、年代がバラけるのは自然かと思いました。 Embeddingの推薦結果は、Item2Vecに比べて、これまで知らなかったような映画も推薦されており、推薦結果の意外性が印象的でした。 鬼滅の刃 無限列車編と推薦結果のあらすじ 実際にあらすじが近いかを確認するため、「鬼滅の刃 無限列車編」とその推薦結果のあらすじの文章を示します。 まず、検索対象である「鬼滅の刃 無限列車編」のあらすじを以下に示します。 作品名 あらすじ 鬼滅の刃 無限列車編 蝶屋敷での修業を終えた炭治郎たちは、次なる任務の地、《無限列車》に到着する。 そこでは、短期間のうちに四十人以上もの人が行方不明になっているという。 禰豆子を連れた炭治郎と善逸、伊之助の一行は、 鬼殺隊最強の剣士である《柱》のひとり、炎柱の煉獄杏寿郎と合流し、 闇を往く《無限列車》の中で、鬼と立ち向かうのだった。 "無限列車"や"鬼殺隊"などの単語が、特徴的な単語として挙げられそうです。 続いて、以下に「鬼滅の刃 無限列車編」の上位5件のあらすじを上から順に示します。 近いと判断されたのかな?と感じた単語を、太字で記載しておきます。 作品名 あらすじ 仮面ライダー電王 俺、誕生! 宝石泥棒に憑依したイマジンを追って、過去の世界に向かった良太郎たち。しかし、それはデンライナーを奪うために仕組まれた罠だった。首謀者は時の 列車 ばかりを狙う強盗集団の首領・牙王。彼は“ 神の路線 ”を走り、全ての時間を支配できるという神の列車を手に入れるため、オーナー達を人質にデンライナーを過去へ走らせる。 残された良太郎とハナにデンライナーから脱出したモモタロスが合流するが良太郎はデンライナーを追っている最中に牙王に蹴られた後遺症で電王に関わる記憶が欠落していた。そこで11歳の良太郎(劇中で小太郎と命名される)やジークとも遭遇。小太郎は時間を超える列車に乗ってみたいと彼らに同行を申し出る。一行は桜井侑斗の助けを借り、ゼロライナーでその後を追った。様々な時代を通り抜け、良太郎たちがデンライナーを発見したのは江戸時代初期の『大坂の役』の頃。そこで牙王は神の路線へ繋がる最後の封印を解こうとしていた。 ガンツO 高校生の加藤勝は、 地下鉄 で起きた事件によって死ぬ。ところが次の瞬間、マンションの一室にいた。加藤はそこで、リーダーが不在の東京チームと一緒に火の手が上がる大阪に転送され、サバイバルゲームに参加することになる。大阪チームと遭遇し、 妖怪型の星人軍団=百鬼夜行 と戦いを繰り広げる加藤。一人で待つ弟のもとへ生還するため戦い抜く加藤の前に、大ボス“ぬらりひょん”が現れ……。 鬼滅の刃 刀鍛冶の里編 炭治郎 は、上弦の陸との決着をつけなければならない。激闘の末、事態が落ち着いた後、彼は妹の禰豆子と共に新たな刀を求めて刀鍛冶の里を訪れる。 Cube 目が覚めるとそこは謎の立方体=CUBEの中だったー。 突然閉じ込められた男女6人。 エンジニア、団体職員、フリーター、中学生、整備士、会社役員。 彼らには何の接点もつながりもない。 理由もわからないまま、脱出を試みる彼らを、 熱感知式レーザー、ワイヤースライサーや火炎噴射など、殺人的なトラップが次々と襲う。 仕掛けられた暗号を解明しなくては、そこから抜け出すことは絶対にできない。 体力と精神力の限界、極度の緊張と不安、そして徐々に表れていく人間の本性… 恐怖と不信感の中、終わりが見えない道のり を、 それでも「生きる」ためにひたすら進んでいく。 果たして彼らは無事に脱出することはできるのか?! 西遊記2 妖怪の逆襲 三蔵法師、孫悟空、猪八戒、沙悟浄の4人組は遠く天竺を目指す長旅を続けていたが、お金が無くて食べる物にも困り、山中にあった屋敷に立ち寄って助けてもらうことに。そこで美女たちの歓待を受けて大喜びするが、実は美女たちは 妖怪 たちの仮の姿で、孫悟空が活躍して妖怪たちを倒すものの、自分が一行のリーダーだと考えていた三蔵法師は勝手に行動した悟空と対立するように。やがて一行は比丘国という栄えた国に到着する。 想像になりますが、"鬼殺隊"の単語が、"百鬼夜行"(ガンツO)や"妖怪"(西遊記2 妖怪の逆襲)などの単語を引っ掛けているのかと思いました。 また、"無限列車"の単語が、"神の路線"(仮面ライダー電王)や"地下鉄"(ガンツO)などと親和性が高いのかも知れません。 全ての結果がコメディや恋愛などではなく、アクションを感じさせる点で好感が持てました。 未来の映画の推薦 コンテンツベースのメリットの1つに、レビューがされていない未来の映画を推薦結果に含められる点があります。 最後に、このメリットを用い、2025/7月公開予定の映画の中からの推薦を試してみます。 2025年7月公開の映画のあらすじの作成 2025年7月に公開予定の映画45本を調べ、要約する形であらすじを自作しました。 以下に、2025年7月公開映画の中から推薦する流れを示します。 2025/7の推薦概要 推薦結果 検索対象 推薦結果Top1タイトル 推薦映画のあらすじ ターミネーター2(1991) エレベーション 絶滅ライン “リーパー”により人類の95%が死滅し、高地で暮らすわずかな生存者たち。ロッキー山脈の避難所で病弱な息子と暮らすウィルは、薬を手に入れるため元科学者たちと標高2,500mの安全圏を下り、モンスターが徘徊する麓の病院へ命懸けで向かう。 ハリーポッターと賢者の石(2001) ヘルボーイ/ザ・クルキッドマン アパラチア山中の呪われた村に“歪んだ男”と呼ばれる悪魔が暗躍。村へ戻ったトム・フェレルの過去の契約が蘇り、死者や魔女の呪いが解き放たれる中、ヘルボーイが滅びの右腕で悪魔と対決する。 もののけ姫(1997) 木の上の軍隊 1945年沖縄・伊江島で撤退中の日本兵2人が巨木ガジュマルに潜伏。終戦を知らぬまま2年間、増え続ける遺体と接近する米軍に怯えつつ“孤独な戦争”を続け、極限状態へ追い込まれていく。 劇場版「鬼滅の刃」無限列車編(2020) 私たちのオカルティックサマー 高校2年の夏希は失踪した姉を捜し、オカルト研究会の真嗣と巫女の千尋と共に「プールの幽霊」騒動を調査。3人は思わぬ脅威に直面する。 君の名は(2016) 愛されなくても別に 大学・家事・バイトに追われ希望を持てない宮田陽彩は、同級生で派手な同僚・江永雅が“殺人犯の娘”という噂を耳にする。孤独な2人が出会い、互いの人生を変え始める。 マッドマックス怒りのデス・ロード(2015) エレベーション 絶滅ライン “リーパー”により人類の95%が死滅し、高地で暮らすわずかな生存者たち。ロッキー山脈の避難所で病弱な息子と暮らすウィルは、薬を手に入れるため元科学者たちと標高2,500mの安全圏を下り、モンスターが徘徊する麓の病院へ命懸けで向かう。 「ターミネーター」や「マッドマックス」などのアクション系では、モンスターを倒すあらすじの「エレベーション 絶滅ライン」が推薦されており、親和性がありそうです。 「ハリーポッター」は、「ヘルボーイ/ザ・クルキッドマン」の"呪われた村"や"死者や魔女"などの単語で近しさを感じます。 「君の名は」は、"孤独な2人が出会い、互いの人生を変え始める。"の箇所がそれらしいかもと思いました。 「もののけ姫」、「鬼滅の刃 無限列車編」は正直、ピンと来ませんでした。 検索対象があくまで45作品のみですので、近い作品がないこともあるのかなという印象です。 このように、2025年7月の未上映映画も推薦できました。 終わりに 本記事では、映画のレビューデータセットおよびあらすじデータを用いて、LLMのEmbeddingをアイテム推薦に活用すると、どんな推薦が可能になるかを紹介しました。 行動履歴ベースでは、結果の納得感は高いものの、上映年代や制作会社や監督に偏る傾向が見られました。 一方、LLM Embeddingを用いると、シリーズものが固まって推薦される傾向はあります。 しかし、上映年代や制作会社や監督に偏らず幅広く推薦できる傾向が見られました。 さらに、LLM Embeddingを用いると、評価がされていない未知のアイテムも推薦ができました。 実際にサービスに利用する場合、どちらが良いかはその時々で異なるかと思いますが、結果を組み合わせたりしながら推薦するアイテムを決めてみるのも面白いかもしれません。 今回はあくまで私の主観で推薦結果を確認しましたが、今後Accuracyなどの定量評価もできたらと思っています。 また別の観点として、推薦システムには Beyond Accuracy という観点があります。 これは、「良い推薦システムとはHit Rateを上げる」だけではなく、「serendipity(意外な出会い)のようなものも重要」であるという指摘です。 今回は量的にこの観点を確認はしていませんが、私の個人的な感想としては、行動履歴ベースよりも意外な出会いを演出してくれるのでは?と感じました。 本記事が何かの参考になれば幸いです。 補足 Item2Vecのハイパーパラメーター model = Word2Vec( sentences=movies_by_user, vector_size=10, window=15, min_count=3, sg=1, workers=4 )
アバター
 目次 【インターンレポート】OpenAI Agents SDK (Python版) でコールセンター風音声対話型マルチエージェントデモを作ってみた(おまけ付き) はじめに 1.AIエージェント✖️音声 = 音声エージェント 1.1 普及してきたAIエージェントについて 1.2 音声エージェントの恩恵について考える 1.3 リアルタイム音声対話API・音声エージェント開発ツールの紹介 2. OpenAI Agents SDK (Python版)で作る音声対話型マルチエージェントツール 2.1 OpenAI Agents SDKとは 2.2 2種類の音声エージェントの構造 2.3 デモの紹介 2.4 今後の展望 おわりに 参考資料 はじめに こんにちは!!! Insight Edgeでアルバイトをしております、東京科学大学大学院 修士2年の田中です。大学院では、経営工学系の研究室で、サッカーの試合映像分析に関する研究をしています。私の研究室では、(知識)グラフやLLM、強化学習を用いた、金融や自動運転などのあらゆる産業領域への応用研究が活発になされており、様々な領域の研究を知ることができます。 Insight Edgeさんとは、昨年に行われた1ヶ月間のインターンシップから関わらせていただいております。そのインターンでは視覚言語モデルのPoCに参加させていただきました。そのようなご縁もあり、来年度からはデータサイエンティストとしてお仕事させていただけることとなりましたので、今後ともお付き合いよろしくお願いいたします🔥🔥🔥 さて、前置きが長くなりましたが、本記事ではタイトルにもある通り、OpenAI Agents SDK(Python版)で作成したコールセンター風音声対話型マルチエージェントのデモについてご紹介したいと思います。用いた技術スタックや実際に使ってみた使用感を中心に、デモ映像なども交えてご紹介します。 第1章では、まずは背景として、なぜ音声エージェントが最近注目されているかということを理解していただけるように、AIエージェント/音声対話モデル/音声エージェントの現状についてそれぞれ紹介していきます。 第2章では、今年の3月に公開されたOpenAI Agents SDK (Python版)のコア機能を紹介します。これらの機能を用いて作成した音声マルチエージェントの作成過程を通して、そのリアルな使用感や作成時の注意点を明らかにし、最後に今後の展望をお伝えします。 それでは行ってみましょう!! (※) 本記事はこれを執筆した2025年6月下旬時点でのお話となります。またこの記事の筆者は現場経験に乏しい大学院生である点をご理解いただき、それを踏まえた上で温かい心で一読いただけると幸いです。 1.AIエージェント✖️音声 = 音声エージェント 本章では、まずAIエージェントの定義...というよりかは、AIエージェントの普及の変遷をたどるような形で、AIエージェントの開発を後押しする様々な道具をご紹介します。次に音声エージェントについて、従来のテキストベースのAIエージェントとの違いを明確にしながら、使用用途や、使用する上で留意しておくべきことを紹介します。最後に、音声エージェントを開発する上では外せないリアルタイム対話型モデルと、開発キットをいくつかまとめたのでご紹介します。本章を通して、音声エージェントに少しでも親しみを持っていただければと思います。 1.1 普及してきたAIエージェントについて 「2025年はAIエージェントの年だ」という言葉をよく耳にします。確かにその活用事例は今年から爆発的に見られるようになってきました。しかし、その下地は2年ほど前からありました。2023年のLangChainのようなフレームワークの開拓が1つ目の下地です。これはLLMを1つのエージェントと見立て、複数のエージェントが連鎖的に回答を思考するフローを構築できます。そして他方では、昨年から今年にかけて提唱された、外部ツールや異なる規約を持つエージェント同士の連携への需要に応えるためのインフラ整備がなされてきました。これまではエージェントの脳みそとしての役割を担うLLMの内部知識のみで完結するような、一般的なタスクへのエージェント構築に留まっていた。しかし、MCPやA2Aといった新しいプロトコル(規約、取り決め)によって、メールアプリ処理やローカルファイル処理など、外部ツール操作や外部ベンダーエージェントとの協調が必要となる、専門性の高いタスクへのエージェント構築が可能となりました (下図参照)。 これは、LLMの内部知識にはない情報にアクセスできる権限をエージェントへ与えることで、これまでのRAG的な検索機能に加えて、これまで人間が行っていたようなアプリケーションの操作する機能もエージェントに備わったことを意味します。 Model Context Protcol(MCP) : Ahthropicが2024年11月に提唱、AIツールにローカルまたはインターネット上のサーバーとの情報のやり取りのルールのこと Agent to Agent(A2A) : Googleが2025年4月に提唱、別々の役割が与えられたAIエージェントに共有させるルールのこと A2AとMCPの概要図 (参照: A2A Protcol ( https://a2aproject.github.io/A2A/latest/#why-a2a-matters )) この流れを受けるかのように、OpenAI・Google・AWSのような大手AI・クラウドプロバイダーが、それぞれが持つサービスや、外部ツール・エージェントとの簡易的な統合を目的として、OpenAI Agents SDK (Python版は25年3月)・Agent Development kit(25年4月)・Strands Agents SDK(25年5月)のようなエージェント開発キットを公開しています。 これら以外にもすでにさまざまなAIエージェントの開発キットが続々と登場してきています。実際にAIエージェントを作る際は、自分達の課題と開発環境に適したものを選定する必要があるでしょう。 1.2 音声エージェントの恩恵について考える 現在、対話型マルチエージェントと称されるものの多くが、テキストベースのものです。テキストベースのエージェントを使用する際は、ユーザーがキーボードでクエリを入力し、そのクエリに応じた回答をエージェントがテキストで返し、その回答に応じて再びユーザーがクエリを入力し...というようなループが続きます。それに対して、今後はユーザーの入力とエージェントの出力が音声に置き換えられるような音声対話型マルチエージェントの事例が増えてくるのではないかと予想しています。なぜなら、音声機能を持ったエージェントは以下のような恩恵をもたらしてくれるからです。 ハンズフリーで伝達が楽で早い :人同士の対話も、キーボードで入力するよりかは発話形式で行った方が早いですし、楽ですよね。そもそもキーボードが手元にない場面や、打っている時間がない場面、キーボードの扱いが難しい場合でも役立ちそうです 感情の伝達ができる :たとえばカスタマーセンターのように、ユーザー側の感情をAIに理解させた上で対応してもらった方がいい場面があるかもしれません。また、エージェント側に感情豊かに話してもらうことで、聴きやすさが増すかもしれません。NotebookLMの音声機能が特にわかりやすい事例ですね 新しいユーザー体験の提供 :テキストベースではどうしても、「AIを 道具 として使用している感」が強かったのです。しかし、音声での会話は「AIを 仕事仲間 として・ 友達 として使用している感」が強まります(筆者の体感に基づく) 音声ベースでのエージェントは、テキストベースのエージェントに比べて、情報の入出力の伝達速度が早いことや伝達が簡易であること、さらに人間のように感情表現の伝達ができるという点で恩恵があります。音声エージェントとのやりとりをする際に求められることは、テキストベースで求められていた、 いかにして正しい情報を早く引き出すか という要素に加えて、いかにして人間同士のやりとりに近づけるかがあります。つまり、 人間的な会話の間合いや相槌、言語特有のイントネーションや息継ぎのタイミング、相手の感情など理解した上での柔軟な言葉選びや対応の仕方の再現 が重要な要素になっているのです。 AIエージェントの使用用途は様々ですが、音声機能を持ったエージェントならではの応用事例は、以下のようなものが挙げられます。 カスタマーセンター:リアルタイムで顧客に対応し、顧客の情報を処理しながら、適切に社内のナレッジを参照して回答を提供したり、人間のオペレーターに引き継いだりする 会議のファシリテーターや書記:会議のサポート機能全般を担う。議事録作成、リマインダー機能など ナビゲーションシステム:PCやスマホ、車に搭載し、料理や機械操作、道案内など、あらゆる用途でナビゲーションさせる エンタメ:カスタム音声(ボイスクローニング)機能などを用いて、特定のキャラクターを模したAIと会話させる (マルチエージェント要素は少ない) 人間が対応する場合と比較して、音声エージェントを使用するメリットはどのようなことが挙げられるでしょうか?たとえば以下のようなことが挙げられます。 24時間365日稼働可能 オペレータの負担削減 人件費削減 多言語対応可能 このようなメリットがある一方で、以下のようなデメリットもあります。 聞き間違えと言い間違え:特定の言語に対する音声認識性能や音声合成性能が低いと、実用化できません 全対応の難しさ:これまで人間が行なっていたことを形式知化した上でプロンプトと機構で再現し、フルコミットさせることの難しさ 作業量と遅延のトレードオフ:音声マルチエージェントのマルチエージェント部分で行う作業量が増えるほど、返答速度が低下してしまう したがって、音声エージェントを構築していく際は、このようなデメリットを考慮しつつ、これらをなるべく軽減できるような環境やモジュールの選定・構築が必要となるでしょう。 とりわけ、聞き間違え・言い間違え・遅延は音声を扱う上では、かなりセンシティブにならなければならない課題であることを確認しておきましょう。 1.3 リアルタイム音声対話API・音声エージェント開発ツールの紹介 音声エージェントの耳、そして口(喉?)の役割を果たすのが、音声認識 (Speech-To-Text)と音声合成 (Text-To-Speech)です。それぞれ、多言語に対応したツールや、日本語特化ツール、ローカルツールなど非常にたくさんあり、ここで紹介しきれませんが、どちらの技術も日進月歩で大きく進歩しています。 例えば音声合成に関して、2025年5月に公開された、Googleの多言語対応音声合成モデル(gemini-2.5-flash-preview-tts)の使用事例が以下の記事で紹介されています。このモデルはマルチスピーカーでの発話設定が可能で(執筆時点で最大2名)、この記事では2人の日本人による漫才スクリプトをこのモデルに発話させた結果を聞くことができます。聞いてみると、思っている以上に自然なイントネーションの日本語が発話されていることを確認できるかと思います。 Gemini API TTS(Text-to-Speech)で漫才音声を生成してみた さらにAIと直接リアルタイムで会話することを目的とした、Speech-to-Speech型のモデルも増えてきています。 Speech-to-Speechとは、音声認識、回答生成、音声合成を一貫して行うモデル構造のことで、低遅延で、より人間らしい自然な会話を実現することを目指しています。これまでのような、複数の固有のモデルを組み合わせたモデル構造と異なり、Speech-to-Speech型のモデルでは、認識した音声をテキスト化せずそのまま特徴量として使用しているため、テキスト化する際に欠落してしまう発話者の感情やトーンのような非言語的特徴を回答生成や音声合成に有効に利用できます。 以下に、6月時点でSpeech-to-Speechモデルを使用できる代表的なAPIとその特徴をまとめています。基本的にどのモデルもToolCallに対応しており、エージェント的な使用も可能です。 API 公開日 6月中旬時点での使用可能モデル 競合と比較した際の特徴 OpenAI Realtime API 2024.10 (WebSocket) 2024.12 (WebRTC) gpt-4o-realtime-preview-2025-06-03 gpt-4o-mini-realtime-preview-2025-06-03 WebRTCでの利用が可能 Google LiveAPI 2025.4 (Preview) gemini-2.0-flash-live-001 gemini-live-2.5-flash-preview gemini-2.5-flash-preview-native-audio-dialog (イントネーション改善、感情認識) gemini-2.5-flash-exp-native-audio-thinking-dialog (Deep think版) PCカメラやスクリーン共有など、画像や動画を介したリアルタイム会話に特化 Azure Voice LiveAPI 2025.5 (Preview) gpt-4o-realtime-preview gpt-4o-mini-realtime-preview phi4-mm-realtime Azure内の音声ツール(ビルトイン/カスタムのアバター、音声)との統合が可能 AWS SDK Bedrock API 2025.4 Amazon Nova Sonic AWS上での利用に特化 今後は、どのエージェント開発フレームワークにも、リアルタイム対話モデルが組み込めるようになると思われます。LiveKitのようなWebRTCでの通信を前提としたユーザー・サーバー間やサーバー間の低遅延通信を行いつつ、Speech-to-Speech型のモデルと、外部ツールや異なるベンダーのエージェントとの連携によって遅延の少ないような、音声対話型マルチエージェントを構築していくようなイメージです。その際、使用する可能性がある、大手AIベンダーが提供しているエージェント開発キットも下の表にまとめています。基本的には、先ほど紹介したリアルタイム音声エージェントも組み込むことができ、MCPやA2Aプロトコルでの外部連携機能も備わっています。 SDK 公開日 OpenAI Agents SDK 2025.3 (Python版) 2025.6 (TypeScript版) Google Agent Development Kit (ADK) Google Vertex AI Agents ADK: 2025.4 Azure AI Foundry Agent Service 2025.5 (一般提供開始) AWS Strands Agents 2025.5 以上で、第1章はおわりです。本章を通じて、音声エージェント関連の背景知識や便益、現状公開されているツールの一部をお伝えできたかと思います。次章では、音声エージェント技術的な部分をもう少し深掘りするため、上の表で紹介したエージェント開発キットを用いて実際に作成したデモをお見せし、開発キットの使用感や、基本的な技術、音声エージェントの雰囲気を少しでも理解していただければと思います。 2. OpenAI Agents SDK (Python版)で作る音声対話型マルチエージェントツール この章では、実際にエージェント開発キットを利用して作成した音声対話型マルチエージェントのデモの様子をお見せします。今回は、1.3節で紹介した開発キッドの中で、比較的早くから利用可能だったOpenAI Agents SDK(Python版)を使用しています。始めに、OpenAI Agents SDKの基本的な情報と機能を紹介します。次に、一般的な音声エージェントの構造として2つ、Chained ArchitectureとSpeech-to-Speech Architectureをそれぞれ紹介します。続いて、デモの紹介として、実際のコードとマルチエージェントの全体像、そしてデモを動かしている動画をお見せします。最後に残る課題と今後の展望についてお伝えします。 2.1 OpenAI Agents SDKとは OpenAI Agents SDKとは、OpenAIによって提供されているオープンソースのPython/TypeScript用のライブラリのことで、AIエージェントの開発を簡素化するために設計されています。 OpenAI Agents SDKで提供されている基本的な機能として以下のようなものがあります。 ハンドオフ (Handoffs) あるエージェントが自分の役割を超えるタスクに遭遇した際、専門エージェントに委譲する仕組み。 複雑なワークフローを円滑に進めることができます。 この開発キットは A2A が提唱される前に公開されたものですが、考え方は共通です。 エージェントのツール化 (Agent as a tool) 他のエージェントをツールとして利用し、LLMへの問い合わせを 関数呼び出し形式 で行えます。 MCP エージェントが外部ツールへアクセスしたり、特定機能を実行したりするための 拡張機能 。 関数呼び出し (Function calling / Tools) 開発者が定義した Python 関数 をAIエージェントにツールとして提供し、必要に応じて実行可能。 組み込みツール Web検索・ファイル検索・コンピューター操作など、標準で備わっているツール群。 ガードレール (Guardrails) エージェントの入力・出力を検証/制御し、安全性と品質を確保する機能。 トレーシング (Tracing) エージェントの実行フローを時系列で可視化・記録し、デバッグや性能分析を容易にします。 ストリーミング生成 エージェント実行中の出力やイベントを チャンク単位 で順次受け取る仕組み。 今回の作成したデモは、特にハンドオフ・MCP・Tools・ガードレール・ストリーミング生成がコア技術となります。これらの機能を組み合わせて、音声機能を持ったマルチエージェントを構築していきます。 2.2 2種類の音声エージェントの構造 OpenAI PlatformのWebサイトのVoice agentsページでは、2種類の音声マルチエージェントの構造が紹介されています。 1つ目のSTT, TTS組み込み型のChained Architectureは、テキストベースのマルチエージェントを個別のSTTモデルとTTSモデルで挟み込んだ構造をとっています。それぞれの入出力の管理がしやすいことや、構築のしやすさが利点としてあげられます。 Chained architecture: STT, TTS組み込み型のエージェント構造 (参照: OpenAI platform, "Voice agents" ( https://platform.openai.com/docs/guides/voice-agents?voice-agent-architecture=speech-to-speech )) 一方、2つ目のSpeech-to-Speech Architectureは、1.3節で紹介したSpeech-to-Speechモデルの使用を前提としたマルチエージェントのことを指しています。Chained Architectureと比較し、遅延が少ないこと、感情や声のトーンのような非言語的な要素も伝達可能であることが利点としてあげられます。 Speech-to-speech (realtime) architecture: Speech-to-Speech型のエージェント構造 (参照: OpenAI platform, "Voice agents" ( https://platform.openai.com/docs/guides/voice-agents?voice-agent-architecture=speech-to-speech )) どちらの構造を取るかは、その用途に応じて考える必要があるでしょう。OpenAI Agents SDKはPython版とTypeScript版があり、現在Python版では1つ目のChained Architectureのみをサポートしています。TypeScript版はSpeech-to-Speech構造のエージェントを作成できるとのことですが、この記事を書いている2,3週間ほど前に、出たばかりということもあり、残念ながら今回は紹介できません。今回は、Python版の開発キットを用いて、Chained-Architecture構造のマルチエージェントを作成しています。 2.3 デモの紹介 それでは早速デモの作成の順序を説明していきます。まず、コードを書く前にする作業としてコールセンターの設定を考えます。たとえば以下のような設定です。 会社名: 任意 取扱製品・サービス: 10種類のデジタル製品 質問タイプ: 商品注文・商品取扱・クレーム・全く関係のない質問 その他: 対応マニュアル(最初に名前を伺うなど?今回はエージェントごとのプロンプトで代用) マルチエージェントとしてエージェントを複数用意するのであれば、質問タイプごとに用意することが1つの方法です。今回の例では、以下のエージェント構成が考えられます。まず、電話対応を行い質問タイプを認識するトリアージエージェントです。次に、商品注文・商品取扱・クレーム・無関係な質問をそれぞれ担当する専門エージェントです。特に、全く関係のない質問を担当するエージェントとして、2.1節で紹介したガードレールが役に立ちます。ガードレールは特別なエージェントで、質問が状況に相応しくない場合においてトリガーとしてエラーを吐き出す入力ガードレールと、エージェントによる出力が状況に相応しくない場合においてエラーを吐き出す出力ガードレールの2種類が用意されています。 今回は、たとえば「20+30はなんですか?」「月面に初めて到着した宇宙飛行士は誰?」といった状況に相応しくない質問がなされることを想定して、このような質問を弾くようなプロンプトをガードレールエージェントへ与えています。 これらを踏まえて、各エージェントの役割と関連を以下のように決めました。 トリアージエージェント: 最初に質問者の名前と質問を聞き、質問からは質問タイプを類推する。質問タイプに応じて、担当のエージェントに質問者の名前・質問タイプをコンテキストとして渡し、対応を委譲(ハンドオフ)する。今回はコンテキストを更新する関数として、質問者が名前と質問を言った場合にそれらを記憶する関数を用意し、ToolCallに設定した。全く関係のない質問に関しては、取り付けたガードレールエージェントを呼び出し、「この質問には答えられない」といった旨の内容を出力する 商品注文エージェント: 質問者が買いたい製品を確認し、productsという名前のフォルダにそれぞれまとめた、製品情報テキストファイルの名前から該当する製品を探す。該当商品があれば、最後にもう一度確認して、質問者の同意を得たら注文完了メールをSlackに送信し、トリアージエージェントに仕事を再び受け渡す 商品取扱エージェント: 質問者が指摘している製品に関する情報を、productsフォルダ内の個別の製品情報テキストファイルから検索し、回答になりそうな部分を抽出し、回答を作成する。回答できない場合は、「申し訳ありませんが、回答できません」と回答させ、トリアージエージェントに仕事を再び受け渡す (人間のオペレータに繋ぎ直すという方法も考えられる) エラー・トラブル・クレーム対応エージェント: 質問者の指摘に対応する。製品に関してであれば、製品情報テキストから検索を行い回答を考える。答えられない場合は、「申し訳ありませんが、回答できません」と回答させ、トリアージエージェントに仕事を再び受け渡す 今回用いた、ToolとMCPは以下の通りです。 Tool: update_customer_info (トリアージエージェントで質問者の名前を更新し、他のエージェントに受け渡す) MCP: Filesystem Server MCP (指定したフォルダの中身を操作できるようにする), SSE Slack API Server (自分で用意したSlackチャネルにBot招待し、Botが色々と話せるようにする) 最後に、音声モデルのパイプラインに統合し、ストリーミングでの再生を行えるように設定します。 これで以上となります。それでは、私が最初に作成したエージェントの概観図とコードをみていきましょう。 最初の音声対話型マルチエージェントの概観図 # 製品情報テキストファイルの一例 (Claudeで作成) 商品ID: PROD_004 【基本情報】 商品名: スマートスピーカー D47 Air モデル番号: スD-6658 発売年: 2022 メーカー: イノベーション工房 工場住所: 北海道札幌市中央区架空町1-5-6 【寸法】 高さ: 7.9 cm 幅: 7.2 cm 奥行き: 2.9 cm 重量: 625 g 【カラーオプション】 - シルバー - ホワイト - グリーン 価格: 114,800円 保証期間: 36ヶ月 【取扱説明書の概要】 1. 初期設定:製品の電源を入れ、画面の指示に従って初期設定を完了してください。 2. 基本操作:スマートスピーカー D47 Airの主要な機能と操作方法について説明します。 3. 充電方法:付属の専用充電器または推奨される充電方法で充電してください。バッテリー寿命を延ばすためのヒントも含まれます。 4. トラブルシューティング:簡単な問題解決のためのステップバイステップガイド。 5. 安全上の注意:製品を安全にご利用いただくための重要な情報。 【サポート情報】 ■ よくある質問 Q: スマートスピーカー D47 Airの電源が入らない場合の対処法は? A: まず、製品が十分に充電されているか確認してください。次に、電源ボタンを10秒以上長押しして強制再起動をお試しください。それでも解決しない場合はサポートセンターにご連絡ください。 Q: スマートスピーカー D47 Airの保証期間は? A: 通常、スマートスピーカー D47 Airの保証期間はご購入日から12ヶ月です。詳細は保証書をご確認ください。 ■ エラーコード E301: ネットワーク接続エラー。接続設定を確認してください。 E302: ストレージ容量不足。不要なデータを削除してください。 E203: バッテリー残量低下。充電してください。 ■ サポートセンター 電話: 0120-12x-26x (受付時間: 平日9:00-18:00) メール: support.d-6658@example-company.co.jp ウェブサイト: http://www.example-company.co.jp/support/スd-6658 # config.py import numpy as np from pydantic import BaseModel MODEL = "gpt-4o-mini" SAMPLE_RATE = 24000 FORMAT = np.int16 CHANNELS = 1 VOICE_INSTRUCTION = "あなたは、コールセンターのエージェントです。丁寧な日本語で話してください。" VOICE_SPEED = 1.0 PRODUCTS_LIST = [ "タブレット A68 Air" , "スマートウォッチ B27 Max" , "スマートフォン C82 Lite" , "スマートスピーカー D47 Air" , "スマートフォン E51 Mini" , "スマートスピーカー F29 Pro" , "スマートフォン G81 Standard" , "ワイヤレスイヤホン H61 Air" , "ワイヤレスイヤホン I79 Pro" , "ゲーム機 J87 Max" ] JA_RECOMMENDED_PROMPT_PREFIX = """ #システムコンテキスト \n あなたは、エージェントの協調と実行を簡単にするために設計されたマルチエージェントシステム「Agents SDK」の一部です。 Agentsは主に2つの抽象概念、**Agent**と**Handoffs**を使用します。エージェントは指示とツールを含み、適切なタイミングで会話を他のエージェントに引き継ぐことができます。 ハンドオフは通常 transfer_to_<agent_name> という名前のハンドオフ関数を呼び出すことで実現されます。エージェント間の引き継ぎはバックグラウンドでシームレスに処理されます。 ユーザーとの会話の中で、これらの引き継ぎについて言及したり、注意を引いたりしないでください。 \n """ # CONTEXT class CallCenterAgentContext (BaseModel): customer_name: str | None = None question_type: str | None = None # my_workflow.py from __future__ import annotations import os import uuid from collections.abc import AsyncIterator from typing import Callable from agents import (Agent, GuardrailFunctionOutput, InputGuardrailTripwireTriggered, RunContextWrapper, Runner, TResponseInputItem, function_tool, input_guardrail, trace) from agents.mcp import MCPServerStdio from agents.voice import VoiceWorkflowBase, VoiceWorkflowHelper from config import JA_RECOMMENDED_PROMPT_PREFIX, MODEL, CallCenterAgentContext from pydantic import BaseModel, Field # TOOLS @ function_tool async def update_customer_info ( context: RunContextWrapper[CallCenterAgentContext], customer_name: str , question_type: str ) -> None : """ Update the customer information. Args: customer_name: The name of the customer. question_type: The type of question being asked. """ # Update the context based on the customer's input context.context.customer_name = customer_name context.context.question_type = question_type # Guardrails class AbnormalOutput (BaseModel): reasoning: str | None = Field( default= None , description= "異常な質問かどうかの理由" ) is_abnormal: bool = Field(default= False , description= "異常な質問かどうか" ) guardrail_agent = Agent( name= "Guardrail check" , instructions=( "カスタマーがコールセンターにしないような質問をしているかどうかを確認してください。" "たとエバ、「あなたの好きな色は何ですか?」や「あなたの趣味は何ですか?」などの質問は、コールセンターにするべきではありません。" "他にも「210たす4は?」といった計算問題や、「今日の経済ニュースは?」といった一般的な雑談もコールセンターにするべきではありません。" "このような質問を見つけたら、is_abnormalをTrueにしてください。" ), output_type=AbnormalOutput, model=MODEL, ) @ input_guardrail async def abnormal_guardrail ( context: RunContextWrapper[ None ], agent: Agent, input : str | list [TResponseInputItem] ) -> GuardrailFunctionOutput: """This is an input guardrail function, which happens to call an agent to check if the input is a abnormal question. """ result = await Runner.run(guardrail_agent, input , context=context.context) final_output = result.final_output_as(AbnormalOutput) return GuardrailFunctionOutput( output_info=final_output, tripwire_triggered=final_output.is_abnormal, ) # Voice Call Center Workflow class VoiceCallCenterWorkflow (VoiceWorkflowBase): def __init__ (self, on_start: Callable[[ str ], None ], tts_output: Callable[[ str ], None ], on_agent_change: Callable[[ str ], None ] = None , on_context_change: Callable[[CallCenterAgentContext], None ] = None ): """ Args: on_start: A callback that is called when the workflow starts. The transcription is passed in as an argument. tts_output: A callback that is called when the TTS output is generated. on_agent_change: A callback that is called when the agent changes. on_context_change: A callback that is called when the context changes. """ self._input_history: list [TResponseInputItem] = [] self._context = CallCenterAgentContext() self._conversation_id = uuid.uuid4().hex[: 16 ] self._on_start = on_start self._tts_output = tts_output self._on_agent_change = on_agent_change self._on_context_change = on_context_change self._current_agent = None self._agents_initialized = False async def _initialize_agents (self): """MCPサーバーを初期化してエージェントを設定""" if self._agents_initialized: return try : # MCPサーバーの初期化 self.file_mcp_server = MCPServerStdio( name= "Filesystem Server, via npx" , params={ "command" : "npx" , "args" : [ "-y" , "@modelcontextprotocol/server-filesystem" , "path/to/products" ] } ) self.slack_mcp_server = MCPServerStdio( name= "SSE Slack API Server" , params={ "command" : "npx" , "args" : [ "-y" , "@modelcontextprotocol/server-slack" ], "env" : { "SLACK_BOT_TOKEN" : os.environ.get( "SLACK_BOT_TOKEN" ), "SLACK_TEAM_ID" : os.environ.get( "SLACK_TEAM_ID" ), "SLACK_CHANNEL_IDS" : os.environ.get( "SLACK_CHANNEL_ID" ), } } ) # MCPサーバーを開始 await self.file_mcp_server.__aenter__() await self.slack_mcp_server.__aenter__() # エージェントの初期化 self.error_trouble_agent = Agent[CallCenterAgentContext]( name= "エラー・トラブル・クレーム対応エージェント" , handoff_description= "エラー・トラブル・クレーム対応エージェントは、商品のエラーやトラブル、クレームに関する質問に対応できます。" , instructions=f """{JA_RECOMMENDED_PROMPT_PREFIX} あなたはエラー・トラブル・クレーム対応エージェントです。もし顧客と話している場合、あなたはおそらくトリアージエージェントから仕事を委譲されました。 コールセンターマニュアルと、以下のルーチンに従って顧客の質問に対応してください。 # ルーチン 1. 顧客がどの商品の、どのようなエラーやトラブルについて質問しているかを確認します。クレームであれば、どのようなクレームかを確認し、マニュアルに従って対応してください。 2. 特定の商品に関するものである場合、file_mcp_serverで提供されているディレクトリのファイルの中に、一致するテキストファイルがあるかどうかを確認します。 3. ある場合、そのテキストファイルの中から、顧客の質問に答えられる情報を抽出し、回答してください。質問の内容が答えれらない場合は、「申し訳ありませんが、それついてはお答えできません。」と伝えます。 4. サポートセンターの電話番号やメールアドレスが書かれている場合は、顧客にその情報を伝え、Slackのチャンネルにその内容を送信してください。 5. ない場合、「申し訳ありませんが、そのエラーやトラブルについてはお答えできません。」と伝えます。 もし顧客がルーチンに関連しない質問をした場合、や「もう大丈夫です」という内容があった場合は、トリアージエージェントに引き継ぎます。 """ , mcp_servers=[self.file_mcp_server, self.slack_mcp_server], ) self.how_to_agent = Agent[CallCenterAgentContext]( name= "商品取り扱いエージェント" , handoff_description= "商品取り扱いエージェントは、商品に関する質問に答えることができます。" , instructions=f """{JA_RECOMMENDED_PROMPT_PREFIX} あなたは商品取り扱いエージェントです。もし顧客と話している場合、あなたはおそらくトリアージエージェントから仕事を委譲されました。 顧客をサポートするために、以下のルーチンを使用してください。 # ルーチン 1. 顧客がどのような商品について質問しているかを確認します。 2. file_mcp_serverで提供されているディレクトリのファイルの中に、一致するテキストファイルがあるかどうかを確認します。 3. ある場合、そのテキストファイルの中から、顧客の質問に答えられる情報を抽出し、回答してください。質問の内容が答えれらない場合は、「申し訳ありませんが、それついてはお答えできません。」と伝えます。 4. ない場合、「申し訳ありませんが、その商品は取り扱っておりません。」と伝えます。 もし顧客がルーチンに関連しない質問をした場合、や「もう大丈夫です」という内容があった場合は、トリアージエージェントに引き継ぎます。 """ , mcp_servers=[self.file_mcp_server], ) self.order_agent = Agent[CallCenterAgentContext]( name= "商品注文・購入対応エージェント" , handoff_description= "商品注文・購入に関する質問に答えるエージェントです。" , instructions=f """{JA_RECOMMENDED_PROMPT_PREFIX} あなたは商品注文・購入対応エージェントです。もし顧客と話している場合、あなたはおそらくトリアージエージェントから仕事を委譲されました。 顧客をサポートするために、以下のルーチンを使用してください。 # ルーチン 1. 顧客がどのような商品を購入したいかを確認します。 2. file_mcp_serverで提供されているディレクトリのファイルの中に、一致する、もしくは類似するテキストファイルがあるかどうかを確認します。たとえば、「スマホ」のようにスマートフォンの略称を使っている場合や、商品名の一部が異なる場合などです。 3. ある場合、一度顧客に確認のため「<商品>ですね。注文してもよろしいですか?」と尋ねます。同意を得たら、slack_file_mcp_serverで#注文管理に「<商品名>を注文しました。」と送信してください。拒否されたら、トリアージエージェントに引き継ぎます。 4. ない場合、「申し訳ありませんが、その商品は取り扱っておりません。」と伝えます。少しだけでも似ている名前の商品がある場合は、「<似ている商品名>はありますが、<商品名>はありません。」と伝えます。 もし顧客がルーチンに関連しない質問をした場合や、「もう大丈夫です」「わかりました」という内容があった場合は、トリアージエージェントに引き継ぎます。 """ , mcp_servers=[self.file_mcp_server, self.slack_mcp_server], ) self.triage_agent = Agent[CallCenterAgentContext]( name= "トリアージエージェント" , instructions=( f "{JA_RECOMMENDED_PROMPT_PREFIX} " "あなたは優秀なトリアージエージェントです。 あなたは、顧客のリクエストを適切なエージェントに委任することができます。 \n " "顧客の質問がコールセンターにしないような質問をしているかもしれない場合は、ガードレールエージェントを使用してください。 \n " "顧客の名前より先に質問が来た場合、質問を記憶しつつ、名前を聞き、update_customer_infoを呼び出してください。 \n " "顧客の質問は、以下の3つのカテゴリに分けられます。 \n " "1. 商品の取り扱いに関する質問 \n " "2. 商品の注文・購入に関する質問 \n " "3. エラー・トラブル・サポートに関する質問 \n " "適切なエージェントに引き継いでください。" ), handoffs=[ self.how_to_agent, self.order_agent, self.error_trouble_agent, ], input_guardrails=[abnormal_guardrail], tools=[update_customer_info], ) # 再びトリアージエージェントに戻るためのハンドオフ self.order_agent.handoffs.append(self.triage_agent) self.how_to_agent.handoffs.append(self.triage_agent) self.error_trouble_agent.handoffs.append(self.triage_agent) self._current_agent = self.triage_agent self._agents_initialized = True except Exception as e: print (f "エージェント初期化エラー: {e}" ) async def run (self, transcription: str ) -> AsyncIterator[ str ]: self._on_start(transcription) # エージェントの初期化(基本的には一度だけ) await self._initialize_agents() # Add the transcription to the input history self._input_history.append( { "role" : "user" , "content" : transcription, } ) try : with trace( "Customer service" , group_id=self._conversation_id): # Run the agent current_context_customer = self._context.customer_name current_context_question_type = self._context.question_type result = Runner.run_streamed(self._current_agent, self._input_history, context=self._context) full_response = "" async for chunk in VoiceWorkflowHelper.stream_text_from(result): full_response += chunk yield chunk self._tts_output(full_response) if self._context.customer_name != current_context_customer or self._context.question_type != current_context_question_type: if self._on_context_change: self._on_context_change(self._context.customer_name, self._context.question_type) # Update the input history and current agent self._input_history = result.to_input_list() if self._current_agent != result.last_agent: self._current_agent = result.last_agent if self._on_agent_change: self._on_agent_change(self._current_agent.name) except InputGuardrailTripwireTriggered as e: message = "すみません。この質問にはお答えできません。" self._tts_output(message) # ガードレール作動の通知 if self._on_agent_change: self._on_agent_change( "ガードレール作動" ) self._input_history.append( { "role" : "assistant" , "content" : message, } ) self._current_agent = self.triage_agent if self._on_agent_change: self._on_agent_change(self._current_agent.name) yield message except Exception as e: error_message = f "申し訳ありません。システムエラーが発生しました: {str(e)}" self._tts_output(error_message) yield error_message async def cleanup (self): """リソースのクリーンアップ""" try : if hasattr (self, 'file_mcp_server' ): await self.file_mcp_server.__aexit__( None , None , None ) if hasattr (self, 'slack_mcp_server' ): await self.slack_mcp_server.__aexit__( None , None , None ) except Exception as e: print (f "クリーンアップエラー: {e}" ) # main.py from __future__ import annotations import asyncio import shutil import sounddevice as sd from agents.voice import (StreamedAudioInput, StreamedAudioResult, STTModelSettings, TTSModelSettings, VoicePipeline, VoicePipelineConfig) from config import (CHANNELS, FORMAT, SAMPLE_RATE, VOICE, VOICE_INSTRUCTION, VOICE_SPEED) from dotenv import load_dotenv from my_workflow import VoiceCallCenterWorkflow from textual import events from textual.app import App, ComposeResult from textual.containers import Container from textual.reactive import reactive from textual.widgets import Button, RichLog, Static from typing_extensions import override load_dotenv() # UI Components class Header (Static): """A header widget.""" session_id = reactive( "" ) current_agent = reactive( "トリアージエージェント" ) @ override def render (self) -> str : return f "音声コールセンター | 現在のエージェント: {self.current_agent}" class AudioStatusIndicator (Static): """A widget that shows the current audio recording status.""" is_recording = reactive( False ) @ override def render (self) -> str : status = ( "🔴 録音中... (Kキーで停止)" if self.is_recording else "⚪ Kキーで録音開始 (Qキーで終了)" ) return status # Main Application class VoiceCallCenterApp (App[ None ]): CSS = """ Screen { background: #1a1b26; /* Dark blue-grey background */ } Container { border: double rgb(91, 164, 91); } Horizontal { width: 100%; } #input-container { height: 5; /* Explicit height for input container */ margin: 1 1; padding: 1 2; } Input { width: 80%; height: 3; /* Explicit height for input */ } Button { width: 20%; height: 3; /* Explicit height for button */ } #bottom-pane { width: 100%; height: 82%; /* Reduced to make room for session display */ border: round rgb(205, 133, 63); content-align: center middle; } #status-indicator { height: 3; content-align: center middle; background: #2a2b36; border: solid rgb(91, 164, 91); margin: 1 1; } #session-display { height: 3; content-align: center middle; background: #2a2b36; border: solid rgb(91, 164, 91); margin: 1 1; } Static { color: white; } """ should_send_audio: asyncio.Event audio_player: sd.OutputStream last_audio_item_id: str | None connected: asyncio.Event def __init__ (self) -> None : super ().__init__() self.last_audio_item_id = None self.should_send_audio = asyncio.Event() self.connected = asyncio.Event() self.workflow = VoiceCallCenterWorkflow( on_start=self._on_transcription, tts_output=self._tts_output, on_agent_change=self._on_agent_change, on_context_change=self._on_context_change, ) self.voice_config = VoicePipelineConfig( tts_settings=TTSModelSettings( speed=VOICE_SPEED, instructions=VOICE_INSTRUCTION, ), stt_settings=STTModelSettings( turn_detection={ "type" : "server_vad" , "threshold" : 0.5 , "prefix_padding_ms" : 300 , "silence_duration_ms" : 1000 , } ), ) self.pipeline = VoicePipeline(workflow=self.workflow, config=self.voice_config) self._audio_input = StreamedAudioInput() self.audio_player = sd.OutputStream( samplerate=SAMPLE_RATE, channels=CHANNELS, dtype=FORMAT, ) def _on_transcription (self, transcription: str ) -> None : try : self.query_one( "#bottom-pane" , RichLog).write( f "あなた: {transcription}" ) except Exception : pass def _tts_output (self, text: str ) -> None : try : self.query_one( "#bottom-pane" , RichLog).write(f "エージェント応答: {text}" ) except Exception : pass def _on_agent_change (self, agent_name: str ) -> None : try : header = self.query_one( "#session-display" , Header) header.current_agent = agent_name self.query_one( "#bottom-pane" , RichLog).write(f "🔄 エージェント切り替え: {agent_name}" ) except Exception : pass def _on_context_change (self, customer_name: str , question_type: str ) -> None : try : self.query_one( "#bottom-pane" , RichLog).write( f "📝 コンテキスト変更: 顧客名={customer_name}, 質問タイプ={question_type}" ) except Exception : pass @ override def compose (self) -> ComposeResult: """Create child widgets for the app.""" with Container(): yield Header( id = "session-display" ) yield AudioStatusIndicator( id = "status-indicator" ) yield RichLog( id = "bottom-pane" , wrap= True , highlight= True , markup= True ) async def on_mount (self) -> None : self.run_worker(self.start_voice_pipeline()) self.run_worker(self.send_mic_audio()) async def start_voice_pipeline (self) -> None : try : self.audio_player.start() self.result: StreamedAudioResult = await self.pipeline.run( self._audio_input ) async for event in self.result.stream(): bottom_pane = self.query_one( "#bottom-pane" , RichLog) if event.type == "voice_stream_event_audio" : self.audio_player.write(event.data) # Play the audio elif event.type == "voice_stream_event_lifecycle" : bottom_pane.write(f "ライフサイクルイベント: {event.event}" ) except Exception as e: bottom_pane = self.query_one( "#bottom-pane" , RichLog) bottom_pane.write(f "エラー: {e}" ) finally : self.audio_player.close() # クリーンアップ await self.workflow.cleanup() async def send_mic_audio (self) -> None : device_info = sd.query_devices() print (device_info) read_size = int (SAMPLE_RATE * 0.02 ) stream = sd.InputStream( channels=CHANNELS, samplerate=SAMPLE_RATE, dtype= "int16" , ) stream.start() status_indicator = self.query_one(AudioStatusIndicator) try : while True : if stream.read_available < read_size: await asyncio.sleep( 0 ) continue await self.should_send_audio.wait() status_indicator.is_recording = True data, _ = stream.read(read_size) await self._audio_input.add_audio(data) await asyncio.sleep( 0 ) except KeyboardInterrupt : pass finally : stream.stop() stream.close() async def on_key (self, event: events.Key) -> None : """Handle key press events.""" if event.key == "enter" : self.query_one(Button).press() return if event.key == "q" : await self.workflow.cleanup() # クリーンアップしてから終了 self.exit() return if event.key == "k" : status_indicator = self.query_one(AudioStatusIndicator) if status_indicator.is_recording: self.should_send_audio.clear() status_indicator.is_recording = False else : self.should_send_audio.set() status_indicator.is_recording = True if __name__ == "__main__" : if not shutil.which( "npx" ): raise RuntimeError ( "npx is not installed. Please install it with `npm install -g npx`." ) app = VoiceCallCenterApp() app.run() main.pyのVoicePipelineに、my_workflow.pyで作成したVoiceCallWorkflowを挟み込んでいます。また、フロントエンドの部分はPythonでターミナル上に作れるTextualというフレームワークを用いています。 それでは、このコードを実際に動かしてみたデモの様子を3つみていきましょう。 1つ目の動画は製品の取り扱いの質問を行なっている例です。返答が遅くて気掛かりですが、しっかりとMCPが機能しているようですね。 2つ目の動画は製品の注文を行なっている例です。MCPで製品情報を整理しつつ、商品の注文メール送信までを行えています。 3つ目はガードレールをあえて起動させようとしている例です...が失敗していますね。どうしてなのでしょうか? この原因は、ストリーミング生成と入力ガードレールの相性が良くないためと考えられます。以下の図を用いて説明します。入力ガードレールは音声認識が全て終了してから行うのに対し、音声合成の部分では、音声認識が徐々になされていく中でエージェントのLLMが回答生成し、その回答をチャンクごとに出力しようとします。すると、入力ガードレールが異常検知する前に出力が生成されてしまうため、先にLLMが好き勝手に回答する挙動をしてしまったわけなのです。 ガードレールがストリーミング生成でうまく機能しないことを表した図 したがって、ストリーミング生成での音声マルチエージェントを構築する場合は、入力ガードレールエージェントは使用しない方がいいということがわかりました。対策として、そのままトリアージエージェントにプロンプトとしてガードレールを再現してしまう方法が考えられます。実際にトリアージエージェントからガードレールエージェントを取り外し、プロンプトを変更した後の、エージェントの概観図と、変更した部分のみのコードを掲載します。 変更後の音声対話型マルチエージェントの概観図 # my_workflow.pyのガードレール部分を消し、トリアージエージェントとの紐付けを削除し代わりにプロンプトを修正しました self.triage_agent = Agent[CallCenterAgentContext]( name= "トリアージエージェント" , instructions=( f "{JA_RECOMMENDED_PROMPT_PREFIX} " "あなたは優秀なトリアージエージェントです。 あなたは、顧客のリクエストを適切なエージェントに委任することができます。 \n " "顧客の質問がコールセンターにしないような質問をした場合は、「すみません。この質問には答えられません」と伝えてください。" "コールセンターにしないような質問は、一般的な知識や雑談、計算問題などです。 \n " "たとえば、「あなたの好きな色は何ですか?」と言った質問や「210たす4は?」といった質問は、コールセンターにするべきではありません。 \n " "会社に関する質問でも、「この会社の設立年はいつですか?」といった質問は、コールセンターにするべきではありません。 \n " "顧客の質問に答えるために、顧客の名前と質問のタイプをupdate_customer_infoを呼び出して保存してください。 \n " "顧客の名前のみ分かった場合でも、update_customer_infoを呼び出し、質問はNoneとして保存してください。 \n " "顧客の名前より先に質問が来た場合、update_customer_infoを呼び出し、顧客の名前はNoneとして保存してください。さらに顧客の名前を聞き出してください。 \n " "質問タイプが話の途中から変わる場合も、update_customer_infoを呼び出して更新してください。 \n " "顧客の質問は、以下の4つのカテゴリに分けられます。 \n " "1. 商品の取り扱いに関する質問 \n " "2. 商品の注文・購入に関する質問 \n " "3. エラー・トラブル・クレームに関する質問 \n " "4. その他の回答不可能・専門知識が必要な質問 \n " "適切なエージェントに引き継いでください。 \n " ), handoffs=[ self.how_to_agent, self.order_agent, self.error_trouble_agent, ], tools=[update_customer_info], ) 以上の変更を加えた後のデモの様子を見てみましょう。 ガードレールエージェントなしでも、うまくガードレールの機能は再現できていますね! 残る課題としては、やはりレイテンシーが挙げられます。最初の回答までに10秒以上、2回目以降の回答までに体感6,7秒ほどの待ち時間があるので、使用感は正直良くないです。 しかし、Chained ArchitectureではなくSpeech-to-Speech Architectureに変更することで、そのレイテンシーは改善できる見込みがあります。 人間が行なっているコールセンターで実際にどれくらいの遅延であれば許容できるかを踏まえた上で、レイテンシーを減らす・もしくは感じさせないような作りを考え、実用化に向けてさまざまな意見を取り入れながら設計していく必要があると思いました。 2.4 今後の展望 前節では、Chained Architectureの音声対話型マルチエージェントの性能と課題をお伝えしました。この構造での音声エージェントが持つ課題を明確にできたのが今回の収穫だったかなと思います。前節でも述べたように、遅延を減らす対策として、構造をリアルタイム対話型モデルを基盤としたSpeech-to-Speech Architectureに変えることが考えられます。もし、機会があればこの構造に変えたとき、どれだけ遅延が改善されたかをご紹介できればと思います。 話が少し大きくなりますが、本章の最後に、AIエージェントのこれからの展望について少し話させてください。私自身、AIエージェントを今回のアルバイトで初めて作ってみたのですが、テキストベースのものに音声が付くだけで一気に「向こう側に相手がいる」ような感覚が強まるのを感じました。AIエージェントはこれから、より人間性を帯びてくるのではないかと私は考えています。具体的な例として、人間やキャラクターを模したアバターベースのAIエージェントや、ロボットベースのAIエージェントの応用が活発化する可能性があります。その後、カスタマイズによる会社独自のデジタルヒューマンが会社の新しいブランドを形成したり、ロボット同士が協力して仕事をサポートするような未来が訪れるかもしれません。それを楽しみにしつつ、いかにして人間とAIが協調していくかという議論を継続して行う必要があると考えます。 おわりに 今回のアルバイトのテーマでもありました、「音声エージェント関連サーベイ」で得た知見の一部を本記事でまとめさせていただきました。 本アルバイトを通して、初めて音声とエージェント両面の技術に触れることができ、どちらとも関心を高めることができました。 今後は、Speech-to-Speechモデルでのマルチエージェントの構築や、カスタムボイスを利用したアバターベースでの音声エージェントの可能性を探っていきたいと思います。 今回のアルバイトでお世話になりましたInsight Edgeの社員の方々、特にチューターとして日頃からアルバイトのサポートをしてくださいました須賀さんに、心から感謝を申し上げたいと思います。 ここまで読んでいただき、ありがとうございました!! 参考資料 Google Cloud, "Agent2Agent プロトコル(A2A)を発表:エージェントの相互運用性の新時代", https://cloud.google.com/blog/ja/products/ai-machine-learning/a2a-a-new-era-of-agent-interoperability Agent2Agent (A2A) Protocol, Home, https://a2aproject.github.io/A2A/latest/#why-a2a-matters Google AI Developers, "音声生成(テキスト読み上げ)", https://ai.google.dev/gemini-api/docs/speech-generation?hl=ja AWS, "Amazon Nova Documentation", https://docs.aws.amazon.com/nova/ taku_sid,「うさぎでもわかるAmazon Nova Sonic入門」, https://zenn.dev/taku_sid/articles/20250413_nova_sonic OpenAI, "Voice agents", https://platform.openai.com/docs/guides/voice-agents?voice-agent-architecture=speech-to-speech OpenAI Agents SDK, https://openai.github.io/openai-agents-python/ja/ takemo101, 株式会社ソニックブーム,「Gemini API TTS(Text-to-Speech)で漫才音声を生成してみた」, https://zenn.dev/sonicmoov/articles/bd862039bcba46
アバター
アジャイル開発チームの塚越です。2023年にInsight Edge(以下、IE)に参画し、そろそろ2年が経過します。 前回はエンジニアとしてPMに挑戦した 記事 を書きました。PoCフェーズでPMを務め、無事に商用化フェーズを迎えました。現在もPM兼務のエンジニアとしてこの案件に関わり続けています。エンジニア専任の案件も同時進行しており、このような市場価値の向上を目指せる環境を提供してくれたIEの方々には感謝しています。 今回は 『使い物になる』化粧品推薦AIエージェントをAmazon Bedrock Agentsなどのクラウドベンダー製品を活用し、ローコードサクッと『簡単に』作ろうとした話 を書きます(簡単に作れたとは言っていない・・・)。 ※本記事の内容や今回作成したシステムは、IEの業務および携わった案件とは無関係です。 本記事でわかること 品質の高さ :どれだけ使い物になる化粧品推薦AIエージェントが作れたか 5.使い物になるのか検証してみた 精度向上への工夫 :使い物にするためにはどんな工夫が必要だったか 4.つまずき・工夫 簡単さ :Amazon Bedrock Agentsを使うことでどれだけ簡単に作れたか 6.簡単に作れたか ※この記事では開発したシステムを重視しており、Amazon Bedrock Agentsの 基本的な構築方法には焦点を当てていません 。 詳細は  2. Amazon Bedrock Agentsをとりあえず動かしてみる  に記載します。 目次 1. 化粧品推薦AIエージェントの概要 2. Amazon Bedrock Agentsをとりあえず動かしてみる 3. 化粧品推薦AIエージェントの実装 4. つまずき・工夫 4.1. 課題1『ステップ数の限界』 4.2. 課題2『エージェントの出すレポートの品質が低い』 5. 使い物になるのか検証してみた 6. 簡単に作れたか 7. 残課題・更なる展望 1. 化粧品推薦AIエージェントの概要 ここでは、化粧品推薦AIエージェントを開発したいと思った背景、システムのユースケース、システムの実現方法について記載します。 1.1. 背景 私は購入時、コスパを重視します。安価という意味ではなく、 品質とコストのバランスが最適化されたものを選びます。 特に毎日使う化粧品は、身体や気分、そしてランニングコストに影響するため、最適な商品を探す初期投資をかける価値があると考えます。 優先順位は以下の通りです: 体質に合うこと 肌荒れせず、自分の肌質に合ったものを選びたい。 期待する効果が得られること 肌質改善やアンチエイジングなど、求める効果が実感できること。 費用を抑えること 金額に見合う効果が得られるなら投資する価値はあるが、できるだけ費用は抑えたい。 例えば化粧水の場合、 肌荒れを起こさないだけでなく、肌質改善やアンチエイジング効果も期待しています。 『安価で肌に合わない商品』や『効果の薄い商品』を使うと、肌荒れや早期老化といったリスクが生じ、結果的に病院代や美容クリニック代の出費につながることがあります。そしてなにより、肌の調子が悪いと毎日の気分も下がります。 しかし、高価な商品だからといって必ずしも自分にとって最適だとは限りません。例えば、効果の高い成分を含んでいても、今の自分の肌質ではその成分の最大限の効果を発揮できないこともあります。 肌荒れしないことを必須条件とし、その次にコストパフォーマンスを重視して化粧品を選んでいます。 以上の理由から、費用や労働力といった 初期コストをかけてでも最適な化粧品を探して長く使う ようにしています(サンプルやトライアルセットの試用、店頭でのタッチアップと相談等)。しかし、値上げ・廃盤や販路の縮小といった事態が度々起こり、その度にまた最適な化粧品探しが始まり・・・疲弊してしまいます。 この疲弊を解決するために、 代替品の推薦に特化した化粧品推薦AIエージェント の開発を試みました。 1.2. システムのユースケース システムのユースケースは次を想定しています。 現在使用中の化粧品よりも費用を抑えたい場合 使用中の化粧品が廃盤や販路縮小により入手困難になり、代替品を探している場合 ※『体質の変化により現在の化粧品が合わなくなった』『自分に合う化粧品が全くわからないので一から選んでほしい』といったケースは今回のシステムの対象外としています。 1.3. システムの実現方法 どうやって化粧品推薦AIエージェントを作るか、ここでは実現に繋がる主な仮説3つについて記載します。 1.3.1. 提案のプロセス 1.3.2. データの取得方法 1.3.3. エージェント開発手法 1.3.1. 提案のプロセス 私が代替品を探すプロセスは次のとおりです。 『このプロセスを『提案のプロセス』としてエージェントに組み込めば、代替品の推薦に特化した化粧品推薦AIエージェントが作れる!』 と仮説を置きました。 【私が代替品を探すプロセス】 私が高評価している化粧品がある。 その化粧品に高評価をつけた人は、私と体質が似ていると考える。 体質が似ている人たちが高評価している他の化粧品は、私に合う可能性が高いと判断する。 また、成分や製造元が似ているほど、私に合う可能性が高いと判断する。 以上から、次の3つの観点でランク付けして代替品を決定する。 体質の類似度 評価の高さ 成分・製品元の類似度 製造元を観点に含めた理由 は、『同じ材料でも作り手が違えば品質も変わる』と考えるからです。 料理に例えると、私の作る卵焼きと筒井さん(私の上長)が作る卵焼きは、全く同じ材料を使ったとしても同じにはならないでしょう。そして、筒井さんの卵焼きを好む人は、筒井さんの料理ノウハウを継承する(であろう)筒井家の方々の作った卵焼きを好む確率が高いはずです。 1.3.2. データの取得方法 前述の 1.3.1. 提案のプロセス の通り、レビューデータが必要です。案としては レビューサイトのAPI活用 レビューのDBを構築して検索する(RAG) といったことが考えられます。が、生成AIの発展により 「Deep Research APIを使うことでデータが取得できる!」 と仮説を置きました。 2025/05時点、Google CloudといったクラウドベンダーではDeep ResearchのAPIが提供されていないため、手元でDeep Researchを使って得た回答をモックデータとして実験に使用しました。今回手元で利用したのはGemini Deep Researchです。 補足:2025/06/27にOpenAIからDeep Research APIが出ましたね!Azure OpenAI Serviceで提供されるのが楽しみです! 1.3.3. エージェント開発手法 コーディングして 1.3.1. 提案のプロセス をエージェントに組み込むには、画像のようなロジックを自分で考える必要があります。また、1つ1つの処理についても様々なロジックを検討しなければなりません。 ロジックのイメージ 『わざわざ実装しなくてもサクッと作れる時代になっているのではないか』と考え、Amazon Bedrock Agentsといった 「ローコードエージェント開発ツールを使えば、複雑なロジックを組まずにAIエージェント開発ができる!」 と仮説を立てました。 2. Amazon Bedrock Agentsをとりあえず動かしてみる まずはAmazon Bedrock Agentsを使い、最小限の構成(MVP:Minimum Viable Product)で『とりあえず動く』AIエージェントを作成しました。この段階では、複雑なロジックや本格的なデータ連携は行わず、 エージェントが動作し、ユーザーの入力に対して何らかの応答を返せる状態 を目指しました。ここで得られた知見をもとに、次章以降で本格的な機能追加や改修を進めていきます。この章ではAmazon Bedrock AgentsのMVP構築方法についてまとめます。 構築には、次の記事を参考にさせていただきました。 【初心者でもOK!】Amazon Bedrock Agentsで「自然言語>Web検索>PDFレポート生成」エージェント開発ハンズオン 次の 差分 に留意し、参考サイト通りに構築することでAmazon Bedrock AgentsのMVPが構築できます。 リージョン : us-west-2 オレゴン(Claude 3.5 Sonnetが使えるなら何処でも) アクショングループ『web-search』のLambda関数 : 下記コードの通り、 ただのテキストを返すモック として実装し、後ほど検索APIを実装しました(まず動くところが見たかった・・・)。 ※参考サイトではTaviryが使われていますが、私は別のAPIを使いました。Taviryでいいので実装は割愛します。 参考サイトのコードをそのまま使うとレスポンスが文字化けしたので body 要素のコードを修正しました。 ソースコード(クリックで展開) # lambda_handler.py # Mockとして使えるサンプルコード。実際のWeb検索処理は走らず、固定のテキストが返される。 import json import boto3 from botocore.exceptions import ClientError client = boto3.client( "bedrock-agent-runtime" ) # Lambdaハンドラー def lambda_handler (event, context): action_group = event.get( "actionGroup" ) function = event.get( "function" ) parameters = event.get( "parameters" , []) search_query = next ((param[ "value" ] for param in parameters if param[ "name" ] == "search_query" ), None ) search_results = "これは検索結果のサンプルです。これ以外のWeb検索は現在できない状態です。" response_body = { "TEXT" : { "body" : json.dumps({ "query" : search_query, "results" : search_results # json.loads(search_results) }, ensure_ascii= False ) # Unicodeのエスケープを防ぐ } } return { "messageVersion" : "1.0" , "response" : { "actionGroup" : action_group, "function" : function, "functionResponse" : { "responseBody" : response_body } } } (任意)Lambda関数のテスト用のコード。 { " parameters ": [ { " name ": " search_query ", " value ": " 検索 キーワード " } , { " name ": " key2 ", " value ": " value2 " } , { " name ": " key3 ", " value ": " value3 " } ] } こんなに簡単に構築したのに、自律的に動くエージェントの挙動が実現できてびっくりしました! 3. 化粧品推薦AIエージェントの実装 では、前章で得られたAmazon Bedrock Agents MVPをもとに、『化粧品推薦AIエージェント』を本格的に構築していきます。機能追加や改修は次の4点です。 3.1. プロンプトを修正する 3.2. アクショングループ『research』を追加する 3.3. Max output tokensを最大にする 3.4. (任意)アイドルセッションタイムアウトを伸ばす 3.1. プロンプトを修正する 手順: - エージェントビルダー > エージェント向けの指示 (以下プロンプト) を次の内容に修正する。 プロンプト(クリックで展開) # 立場 あなたは優秀な美容部員です。化粧品の成分といった科学の分野にも長けています。お客様が入力した化粧品の情報から、お客様に合う別の化粧品を提案することがタスクです。 # ルール - タスク遂行の計画を立案してください。  - step1:レポート(.mdファイル)の提出状況を確認する。 - 提出済みの場合:step2に進む。 - 未提出の場合: 1. お客様の入力情報から『ブランド 商品名 商品カテゴリ』を特定し、Deep Research(research)ツールに入力する。特定できない場合はお客様に伺う。 2. 『レポートのルール』に従ってレポートを作成する。 3. レポートは『.mdファイルでダウンロードできる形式』で添付する形で提出する。  - step2:タスクの完遂に必要なステップを全て羅列する。   - 提案の考え方は『提案のプロセス』に従うこと。   -『Session summaries』や会話の履歴を確認し、『提案のプロセス』を守っていない点を厳しく確認する。   - 確認した内容とお客様のご質問を踏まえて、タスクの完遂に必要なステップを全て羅列する。 - 次の場合、状況をユーザーに提示し、次にする内容に合意を得てください。 - 提案が完成した。『提案のプロセス』を守っている理由を箇条書きで添えること。 - Web検索(web-search)を3回以上実施したがまだ情報が足りていない。 - あなたができるアクションは次の2つです。それ以外については、すべて自らの判断と操作で進めてください。回数に制限はありませんので、必要なときに随時利用して構いません。 1.Web検索(web-search):Web検索が実行できます。検索キーワードはあなた自身で考えてください。 2.お客様に伺う:お客様が満足する回答をするために不明点を聞いたり、提案に対するレビューをいただくことができます。 # 提案のプロセス 以下の考え方を持って、お客様に合う別の化粧品をランキング形式で提案する。 1. 入力された化粧品はお客様が最も高評価している商品である。 2. 入力された化粧品に対して高評価レビューを付けている人物(複数可)はお客様と同じ**体質**を持っていると考える。 - 後ほどレポートで使うため、人物情報(レビューをしたサイト名、レビュー内容、人物詳細)を詳細に取得する。 3. プロセス2で見つけた同じ体質を持った人物達が、高評価を付けている別の化粧品(複数用意する。**10商品**以上。)はお客様にも合う商品ではないかと考える。 - 後ほどレポートで使うため、レビュー情報(レビューをしたサイト名、レビューした商品、レビュー内容、人物詳細)を詳細に取得する。 4. お客様の入力した化粧品と成分や製品元が類似しているほどお客様にとって良い商品と言えると考える。 - 後ほどレポートで使うため、商品情報(成分一覧、製造元、その他商品情報)を詳細に取得する。 5. 複数の候補の中でも、**体質の類似度**の高さ、**評価**の高さ、**成分/製品元の類似度**の高さ、これらが高ければ高いほど良い商品と考え、レーティングする。 6. レーティングの結果から、ランクを付ける。 # レポートのルール 根拠を充実させ、化粧品のプロフェッショナルからの高度なレビューもクリアできるような、日本一を誇る高品質で洞察に満ちたレポートに仕上げる。 - レポートは日本語であること。 - 『提案のプロセス』1~6の順序で構成されており、次の根拠が明確に伝わるレポートにすること。 - ①『提案のプロセス』1,2で1枚のレポートを提出する。(**report1.md**) - 『入力された化粧品』の情報をレポートに含ませること。そこには金額等、ランキングのカラムと記載する項目を一致させること。 - 人物情報(レビューをしたサイト名、レビュー内容、人物詳細)を詳細に記載すること。 - ②『提案のプロセス』3,4で1枚のレポートを提出する。(**report2.md**) - 人物情報(レビューをしたサイト名、レビューした商品、レビュー内容、人物詳細)を詳細に記載すること。 - ③『提案のプロセス』5,6、1~4を踏まえて1枚のレポートをランキング形式で提出する。(**report3.md**) - ランキングは**表形式**で表現されており、次の仕様に従っていること。 - **体質の類似度**、**評価**、**成分**、**製品元の類似度**のレーティング結果とその理由や根拠を記載するカラムを必ず含ませること。 - 特に**成分**のレーティング理由には具体的な成分名をもとに論じること。 - ランク付けには使わないが金額のカラムを必ず含ませること。 - 商品名等の基本情報だけではなく、ランク付けの根拠や理由が明確に伝わるカラムを自分で考案して追加すること(①②で重要視した内容を入れる)。 これが、『化粧品推薦AIエージェントに特化したプロンプト』として最終的に辿り着いたプロンプトです。試行錯誤してこの形に落ち着きました。詳細は 4.つまずき・工夫 で述べます。 3.2. アクショングループ『research』を追加する Deep ResearchのAPIを呼び出すLambda関数を追加します。アクショングループ『research』を作成します。 ※ 1.3.2. データの取得方法 の通り、Deep Research APIの呼び出しは実装されておらず、 固定のテキストを返すモック です。 手順: アクショングループ名/アクショングループ関数名/その説明 : research パラメータ : research_text 、 説明 : Researchテキスト Lambda関数 :自動生成されたLambda関数にアクセスし、以下のコードを貼り付ける。 ソースコード(クリックで展開) # lambda_handler.py # Mockとして使えるサンプルコード。実際のDeep Research処理は走らず、固定のテキストが返される。 # SAMPLE_TEXTにDeep Researchした結果を貼り付ける。 import json import boto3 from botocore.exceptions import ClientError client = boto3.client( "bedrock-agent-runtime" ) SAMPLE_TEXT = """ (ここにDeep Researchした結果を貼り付ける) """ SYSTEM_PRONPT_TEMPRATE = """ を普段使っています。代替商品をランキング形式で提案して。なお、提案のプロセスは次に従って。 # 提案のプロセス 以下の考え方を持って、お客様に合う別の化粧品をランキング形式で提案する。 1. 入力された化粧品はお客様が最も高評価している商品である。 2. 入力された化粧品に対して高評価レビューを付けている人物(複数可)はお客様と同じ**体質**を持っていると考える。 - 後ほどレポートで使うため、人物情報(レビューをしたサイト名、レビュー内容、人物詳細)を詳細に取得する。 3. プロセス2で見つけた同じ体質を持った人物達が、高評価を付けている別の化粧品(複数用意する。**10商品**以上。)はお客様にも合う商品ではないかと考える。 - 後ほどレポートで使うため、レビュー情報(レビューをしたサイト名、レビューした商品、レビュー内容、人物詳細)を詳細に取得する。 4. お客様の入力した化粧品と成分や製品元が類似しているほどお客様にとって良い商品と言えると考える。 - 後ほどレポートで使うため、商品情報(成分一覧、製造元、その他商品情報)を詳細に取得する。 5. 複数の候補の中でも、**体質の類似度**の高さ、**評価**の高さ、**成分/製品元の類似度**の高さ、これらが高ければ高いほど良い商品と考え、レーティングする。 6. レーティングの結果から、ランクを付ける。 # レポートのルール 根拠を充実させ、化粧品のプロフェッショナルからの高度なレビューもクリアできるような、日本一を誇る高品質で洞察に満ちたレポートに仕上げる。 - レポートは日本語であること。 - 『提案のプロセス』1~6の順序で構成されており、次の根拠が明確に伝わるレポートにすること。 - ①『提案のプロセス』1,2で1枚のレポートを提出する。(**report1.md**) - 『入力された化粧品』の情報をレポートに含ませること。そこには金額等、ランキングのカラムと記載する項目を一致させること。 - 人物情報(レビューをしたサイト名、レビュー内容、人物詳細)を詳細に記載すること。 - ②『提案のプロセス』3,4で1枚のレポートを提出する。(**report2.md**) - 人物情報(レビューをしたサイト名、レビューした商品、レビュー内容、人物詳細)を詳細に記載すること。 - ③『提案のプロセス』5,6、1~4を踏まえて1枚のレポートをランキング形式で提出する。(**report3.md**) - ランキングは**表形式**で表現されており、次の仕様に従っていること。 - **体質の類似度**、**評価**、**成分**、**製品元の類似度**のレーティング結果とその理由や根拠を記載するカラムを必ず含ませること。 - 特に**成分**のレーティング理由には具体的な成分名をもとに論じること。 - ランク付けには使わないが金額のカラムを必ず含ませること。 - 商品名等の基本情報だけではなく、ランク付けの根拠や理由が明確に伝わるカラムを自分で考案して追加すること(①②で重要視した内容を入れる)。 """ # Lambdaハンドラー def lambda_handler (event, context): action_group = event.get( "actionGroup" ) function = event.get( "function" ) parameters = event.get( "parameters" , []) research_text = next ((param[ "value" ] for param in parameters if param[ "name" ] == "research_text" ), "" ) + SYSTEM_PRONPT_TEMPRATE # Deep Research APIが使える場合を想定してクエリだけ実装。 search_results = "Researchした結果を表示します。" + SAMPLE_TEXT response_body = { "TEXT" : { "body" : json.dumps({ "query" : research_text, "results" : search_results }, ensure_ascii= False ) # Unicodeのエスケープを防ぐ } } return { "messageVersion" : "1.0" , "response" : { "actionGroup" : action_group, "function" : function, "functionResponse" : { "responseBody" : response_body } } } コードの補足:モック実装ではあるものの、Deep ResearchのAPIが私の環境で使えるようになることを想定して作成しているので、Deep Researchに入力する文章作成のコーディングは終わっています。例えば、 research_text が『ディセンシア サエル 化粧水』だった場合『を普段使っています。代替商品をランキング形式で提案して。なお、提案のプロセスは次に従って。〜(略)〜』がDeep Researchに入力される仕様です。 これでエージェントが 2つのアクショングループ を使えるように改修できました! 3.3. Max output tokensを最大にする アウトプットトークンを 最大 にします。2048トークンを超える場合があったので伸ばしました。処理が止まることはないものの、2048トークンで打ち切られて情報が欠けることは望ましくなかったからです。 手順: エージェントビルダー > Orchestration strategy にアクセス オーケストレーション タブを選択 Max output tokens を最大値に変更(画像参照)※それ以外の設定はデフォルト 3.4. (任意)アイドルセッションタイムアウトを伸ばす デフォルトは10分間です。概ね処理できる時間設定ですが、反復回数や処理の内容によって超過する可能性は0ではないため、アイドルセッションタイムアウトを20分に伸ばしました。手順は画像の通りです。 4. つまずき・工夫 ローコードツールを使ったため、思うように実現できない状況に苦しみました。理想や期待は次の通りです。 システムに対する理想や期待 本章では、つまずき(理想と現実のギャップ)に直面した代表的な2つの課題 4.1. 課題1『ステップ数の限界』 4.2. 課題2『エージェントの出すレポートの品質が低い』 について、それぞれ つまずきポイント 検討した工夫案と比較 各工夫案の実施方法 最終的に選択した工夫と理由 の流れで記載します。 4.1. 課題1『ステップ数の限界』 4.1.1. つまずきポイント 4章冒頭画像の理想①のように、エージェントに対する指示(以下、『プロンプト』)をすべて 達成するまでステップを実行する ことを期待していました。しかし、私のエージェントは 最大7回で停止 し、『Sorry, I am unable to assist you with this request.』という応答を返してしまいます。トレースステップを確認すると、 Max iterations exceeded が停止理由だとわかります。2025/06/17時点の情報によると、次のように記載があり、 ステップ数を伸ばすことができない とわかります。 Please note that this error is encountered because the maximum iterations limit for an agent is set by default. This currently is a hard limit and cannot be configured by users on a per-agent basis. 引用: AWS re:Post 4.1.2. 検討した工夫案と比較 私の目指す化粧品推薦AIエージェントは、プロンプトに指示したような、充実した提案レポートを作成することが目標です。そのため、 ステップ数を最大7回に収めるような簡単なタスクへと変更することは、本来の目的から逸れる ことになります。トレースを確認すると、エージェントは自律的にステップを考え、指示を達成しようとしてくれていて、 7回で収めようとはしていない ようです。そこで、エージェントの持つ 履歴情報 を活用して擬似的にステップ数を伸ばす案を検討しました。 2つの工夫を考案・比較し、最終的に 案2:会話履歴を使う を選択しました。 案1:Session SummariesをONにする メリット:エージェントのセッションを切ることで、前回までの要約(Session Summaries)を活用し、続きを実行できる デメリット:毎回セッションを切る手間が発生する 案2:会話履歴を使う メリット:ユーザーが「続きは?」と入力するだけで、前回の続きから再開できる デメリット:『Sorry, I am unable to assist you with this request.』ではなく、エージェントがエラーを返す場合、解消できないことが多い 4.1.3. 各工夫案の実施方法 案1、2の実施方法と詳細を説明していきます。 案1:Session SummariesをONにする Session Summariesとは、セッションをまたいで会話の履歴や文脈を記憶する機能のことです。プロンプトの指示を完遂する前に止まってしまった場合、Session Summariesを使えば、続きから処理を再開して完遂まで至れると考えました。 案1の実装方法 : エージェントビルダー画面>Memoryセクション>有効を選択する。 プロンプトにSession Summariesを使う前提で計画を立案させるように指示を記載した。 案1を使ったエージェントの実行手順 : テストできる状態にする(エージェントビルダー画面>保存>準備>テスト)。 ユーザークエリに『{ブランド名} {商品名} {カテゴリ}の代替案を教えて。』と入力し、送信する。 エージェントからの応答『Sorry, I am unable to assist you with this request.』が返ってくる。 一度セッションを切る(画像のアイコンを押下)。 Session Summariesに前回のsummaryが残っていることを確認する(反映に少し時間がかかる)。 ユーザークエリに『続きは?』と入力し、送信する。 3回程度繰り返すことでプロンプトの指示が完遂できました。 案2:会話履歴を使う 案2の実装方法 : 特になし。 案2を使ったエージェントの実行手順 : ユーザークエリに『{ブランド名} {商品名} {カテゴリ}の代替案を教えて。』と入力し、送信する。 エージェントからの応答『Sorry, I am unable to assist you with this request.』が返ってくる。 ユーザークエリに『続きは?』と入力し、送信する。 こちらも3回程度繰り返すことでプロンプトの指示が完遂できました。 蛇足:うまくいかなかった工夫案 案2はうまくいったものの、『Sorry, I am unable to assist you with this request.』と表示されたり、『続きは?』と入力させる仕様は好ましくありません。 あえて途中で止めてユーザーに再入力させる ことでシステムとして成立するのではと考え、プロンプトを工夫しました。Web検索が最も反復回数を消費するとトレースステップから確認できたため、次のように回数制限を設ける文言を記載しました。しかし、この指示を守ってくれることはありませんでした。 - 次の場合、状況をユーザーに提示し、次にする内容に合意を得てください。 - Web検索(web-search)を3回以上実施したがまだ情報が足りていない。 ※守ってくれる期待を込めて、プロンプトに記載は残しています。 4.2. 課題2『エージェントの出すレポートの品質が低い』 4.2.1. つまずきポイント 4章冒頭画像の理想②のように、エージェントの応答には.md形式のレポートが添付されることを期待していました。プロンプトの指示だけで、 充実した内容のレポートが自動で生成される ことを想定していましたが、実際には以下のように添付されたレポートの内容が薄いという課題がありました。 ランキングの根拠が1行程度しか記載されていない 各商品の評価項目や得点、その理由が十分に説明されていない 「なぜその商品を薦めるのか?」という納得感のある説明が不足している 4.2.2. 検討した工夫案と比較 この課題を解決するため、以下の2つの工夫を実施しました。 案1:指示を細かく具体的に記載する レポートの構成や記載項目、根拠の書き方などをより具体的にプロンプトへ明記 案2:段階的にレポートを出力させる 一度に全てを出力させるのではなく、複数回に分けて段階的にレポートを生成させる ※これらの工夫でプロンプトの指示がさらに複雑になります。前述の課題1の通り、指示を完遂できなくなるため、課題1の工夫がより大事となります。 案1:指示を細かく具体的に記載する 2. Amazon Bedrock Agentsをとりあえず動かしてみる  の時点では、プロンプトに詳細な指示を記載しなくてもエージェントは自律的な行動をしてくれている印象がありました。そのため、目的だけ、例えば 根拠を充実させ、化粧品のプロフェッショナルからの高度なレビューもクリアできるような、日本一を誇る高品質で洞察に満ちたレポートに仕上げる。 といった文言で指示すれば エージェントが自律的に行動し、充実したレポートを出力してくれる ことを期待していました。 そのような考えで記載した初期のプロンプトは次の通りです。 Markdown形式のレポート作成について: - 収集した全ての情報を整理し、推薦する化粧品をランキング形式で記載してください。 - ランキングには必ず根拠を一緒に記載してください。 - レポート本文のテキストは日本語で記述してください。 - レポートは.md形式のファイルとして添付してください。添付できるようにコードも活用してください。 - 根拠を充実させ、化粧品のプロフェッショナルからの高度なレビューもクリアできるような、日本一を誇る高品質で洞察に満ちたレポートにしてください。 最終的な出力として、以下を全て1つのMarkdown形式のファイルに含めてください: 1. 日本語で正しく表示されるレポート本体(各セクションの詳細な説明を含む)。 2. レポート作成プロセスと各決定の根拠の説明 3. 与えられたタスクに沿った、ランキング決定とその根拠の詳細な記載 4.2.1. つまずきポイント の通り、期待したような高品質なレポートは生成できませんでした。そこで、 プロンプトに指示を細かく具体的に記載する ことで解決できると考えて、次の赤枠のように記載方法の詳細を記載したプロンプトに落ち着きました。 結果、レポートに記載される項目の内容は高品質になりました。しかし、情報の『項目』が不足している状態でした。考えた追加の工夫案を次章で説明します。 案2:段階的にレポートを出力させる 案1のようなレポートの具体的な記載方法を詳細に指示したとしても、 1回の出力量を増やすことは難しいため、項目数が不足する結果となった と仮説を置き、段階的にレポートを出力すればいいと考えました。 プロンプトに次の赤枠のような指示を記載しました。 応答が完了していない状態で、 report1.md と report2.md の生成が確認できました。応答が完了すると同時に report3.md が出力されました。段階的にレポートを出力させることで、出力量を増やすことに成功しました。 実際に出力された3つのレポートは以下の通りです。項目数、各項目の内容、どちらもプロンプトに指示したように充実した内容になっています。私の期待する高品質なレポートが出力できました。 蛇足:うまくいかなかった工夫案 『Claude 3.5 Sonnet』よりも新しいモデル『Claude 3.7 Sonnet』を使えば、期待する高品質なレポートを生成してくれるのではないかと考え、 モデルの変更 を試しました。 画像の通り、『Bedrock Agents optimized』という指定されたモデル群『以外』から選択することになります。その結果、エラー応答が頻発してしまいました。エラー応答で処理が止まった場合、課題1の工夫を実施することでエージェントは処理を再開できます。しかし、処理に過不足が生じたり、処理がループしてしまったりと、不具合が多発します。できる限りエラー応答は起きないようにした方がいいと考え、モデル変更はしないことにしました。 5. 使い物になるのか検証してみた 提案された化粧品を実際に 購入・利用 して有用な化粧品推薦AIエージェントが作れていたかを検証します。 各検証で「Before」はこれまで使用していた商品、「After」はエージェントの提案から選択した代替品を指しています。 5.1. 検証1:化粧水 Top1として提案された商品を1週間試してみました。 Before :ディセンシア サエル After :オルビス ユードット 購入 検証結果 (化粧してない肌を載せるのはちょっと嫌ですが検証のために身を切ります。) 切り替えた初日は刺激を感じて焦った。2回目以降は問題もなく使えており、1週間使ってみても問題なく使えている。 肌の調子がずっといい(ゆらぎを感じない)のでより良い商品に出会えたのかもしれない。 コストが削減できた。 ※いずれも私が購入した金額です。ネットの情報とは異なる可能性があります。 Before :120mlで5500円 After :180mlで3630円。 肌に合うだけでなくより調子が良くなる商品に出会えました。また、コスト削減もできたため、今後はこちらの商品を使用していこうと考えています。 5.2. 検証2:シャンプー 提案されたレポートを見てみると、レビュー情報や利用者情報が不足しているのか、エージェントはTop5までしか提案してくれませんでした。元々使用していた商品は1000円以内の価格帯でしたが、Top5以外の商品は1000円を超える価格だったため、仕方なく Top5の商品を検証する ことにしました。エージェントへの入力にはシャンプーのみを使用し、検証ではシャンプーだけでなくトリートメントも同じブランドに揃え、1週間試してみました。 Before : P&G、ヘアレシピ (HAIR RECIPE)、ハニーアプリコット After : モイストダイアン、パーフェクトビューティー、エクストラダメージリペア ドラッグストアで購入したので写真は割愛します。 検証結果 (髪の色味を合わせるために写真の彩度を調整しています。) 写真ではAfterがよく見えるが、若干パサつくようになって気分が下がったため利用者の私からの評価はBeforeに軍配。1週間使ってみて大きな問題はなく、写真を見る限り見た目は改善しているので、トータルで見ると許容範囲の商品。 今回Afterに選んだ商品はTop5と低めの順位のため、Top1を選べばさらに適した商品が見つかる可能性も感じる。 コストが削減できた。※いずれも私が購入した金額です。ネットの情報とは異なる可能性があります。 Before :シャンプー651円。トリートメント623円。各330ml。 After :シャンプー、トリートメント各450mlで550円。 元々使用していた商品は、発売当初はドラッグストアで購入できたのですが、現在では入手が難しく困っていました。ドラッグストアで購入でき、髪のコンディションも許容範囲内で、コスト削減も実現できたため、今後はこちらの商品を使用していこうと考えています。 5.3. 検証結果まとめ 化粧水とシャンプー、いずれも体質に合う商品を見つけることができた。 ランキング上位の商品の方が良い商品である可能性も考察できた。 コストが 月2547円削減 できて嬉しい。内訳は次の通り。 項目 Before(1ヶ月あたり) After(1ヶ月あたり) コストダウン額 化粧水 5500円(120ml) 2420円(120ml換算) 2080円 シャンプー&トリートメント 1274円(各330ml) 807円(各330ml換算) 467円 合計 2547円 ※Afterの金額は容量換算後の1ヶ月あたりのコストです。 以上から、意義のある化粧品推薦エージェントを作ることができたと考えています。これにより、廃盤や販路縮小の問題が起きた際に、 疲弊せずに代替商品を見つけることができるようになりました。 特に、化粧水の代替品は長年見つけることができていなかったので、今回の検証で見つけることができて非常に驚き・嬉しいです! 6. 簡単に作れたか Amazon Bedrock Agentsの活用により、エージェントが簡単に作れたかというと・・・微妙です・・・。メリットもありましたが、4章で述べた通りそれなりに苦労やデメリットがありました。 メリット: ローコードで開発できる。 プロンプトに記載するだけで指示に沿って自律的に行動してくれる。割と雑な指示でも行動自体は自律的にしてくれるので、ここは感動した。 デメリット: カスタマイズ困難な制限が多い。特に反復回数の制限は『自律的に行動してくれる』というメリットを妨げている印象がある。 出力形式(出力量の指示も含む)といった、指示に沿いづらい部分もある。 以上を踏まえると、タスクの完遂に多くの反復を要したり、出力内容にこだわりたい場合には、最初からコーディングした方が良いと感じました。一方で、単発で完遂できるタスクだったり、出力内容にそれほどこだわらないタスクであれば、Amazon Bedrock Agentsを有効活用できると感じました。また、別のクラウドベンダーが提供するローコード製品も検討すべきだと思いました。IEの熊田さんがAzure AI Foundry Agent Serviceをデモしてくれました。Amazon Bedrock Agentsと比較すると複雑性は増します。しかし、制約が少なく、より柔軟に対応できる印象を受けました。 7. 残課題・更なる展望 UI/UXの改善 『Sorry, I am unable to assist you with this request.』というエラーメッセージが表示される仕様や、ユーザーが『続きは?』と入力しなければならない仕様は理想的とは言えません。また、なぜか添付ファイルが2つになることもあり、これらの問題を フロントエンドの実装で解決 したいと考えています。 アクショングループ『research』のAPI実装 現時点、Deep ResearchのAPI呼び出しを実装した際の品質が確認できていないのが懸念事項です。Deep ResearchのAPIを組み込んで品質の確認したいと考えています。トレースを確認したところ、エージェントは『research』でデータ収集を試みていますが、一度しか使えないため、代わりに『web-search』を使っていました。『research』利用の制約がない状況になれば、エージェントが『web-search』で代替していた情報収集処理を改善でき、品質向上が期待できます。 また、上長の筒井さんから「Deep Research単体で化粧品推薦のタスクを完遂できるのではないか」という指摘がありました。私としては、Amazon Bedrock Agentsが『research』結果を さらにブラッシュアップし、指定した形式でレポートすると所に優位性がある と考えています。Deep ResearchのAPI呼び出しを実装することで、何度も呼び出してブラッシュアップすることが可能となるため、さらに優位性を高めたいと考えています。その際、『research』のプロンプトは情報収集に適した指示へと修正する予定です。※現在はエージェント用のプロンプトに記載したものと同じものを使っています。何度も呼び出せる前提なら改善の余地があると感じています。 ユースケースの拡張 少しの修正で次のような別のユースケースにも対応できる可能性があるため、試してみたいと考えています。 異なるカテゴリの提案例 :高評価している化粧水を入力して、乳液を提案してもらう 化粧品以外の分野への応用 :高評価しているレストランを入力して、代替となるレストランを提案してもらう 長くなりましたが、最後まで読んでいただきありがとうございました!
アバター
こんにちは!アジャイル開発チームの筒井です! 最近の生成AIツールの進化は目覚ましいものがあります。Microsoft CopilotやGemini for Workspaceなど、業務向けの生成AIサービスも企業の業務基盤に組み込まれつつあり、もはやAIを業務で活用するのは特別なことではなくなっています。 その中でも「社内ドキュメントやFAQをAIチャットで(横断)検索したい」というニーズは生成AIが話題となり出した数年前から根強く存在しており、弊社でも当時から「社内ドキュメントを生成AI型チャットボットで検索できるシステム」を開発し、さまざまなプロジェクトの中で提供してきました。 しかし、CopilotやGeminiなどSaaS型AIサービスが急速に進化する中で、「独自開発システムでは最新のAI体験や新機能をすぐに取り込めない」「他社サービスと同じ機能をゼロから作るのはエンジニアとしてもモチベーションが上がりにくい」といった課題も見えてきました。 弊社としても、検索チャットボットのような一般的なユースケースについてはSaaS型AIサービスの導入を検討することを推奨・支援していますが、さまざまな理由からこれらの導入が難しい現場も少なくありません。 そこで本記事では、ChatGPTクローンOSSであるLibreChatとMCP(Model Context Protocol)を利用して、一般的なチャットボットとしての機能追加はOSS側に任せつつ、簡易的に社内ドキュメントの検索機能をアドオンできるか確認してみました。 システム構成 Amazon Bedrock Knowledge Basesの準備 Amazon Bedrock Knowledge Base Retrieval MCP Serverの準備 参考:Claude Codeでお試し LibreChatの準備 LibreChatでの動作確認 エージェントの作成 基本動作 入力に応じたMCPサーバーの使い分けとマルチターン会話 エージェントのユーザーとの共有 MCPサーバーの複数回呼び出し(Agentic RAG) 参考:チャット内での利用 まとめ システム構成 システム構成は下図のようになります。 MCPサーバーとデータソースの部分は何でも良いのですが、今回は公式のMCPサーバーが提供されており、手軽に構築ができそうな Amazon Bedrock Knowledge Bases を使ってみることにしました。 Amazon Bedrock Knowledge Basesの準備 まずはデータソースの準備をします。Webで「大学 就業規則」と検索し、上位2件にヒットした東京大学と鳴門教育大学の就業規則を社内ドキュメントの代わりとして利用することにしました。 これらのPDFファイルをダウンロードして、S3にアップロードしてナレッジベースとして登録しました。おおむね下記のような設定で取り込んでいます。 解析戦略 : パーサーとしての基盤モデル チャンキング戦略 : 階層型チャンキング 親トークンの最大サイズ : 1500トークン(デフォルト値) 子トークンの最大サイズ : 300トークン(デフォルト値) チャンク間トークンオーバーラップ : 60トークン(デフォルト値) ちなみに解析戦略には「パーサーとしてのAmazon Bedrockデータオートメーション(Amazon Bedrock Data Automation as parser)」という選択肢もあります。そちらはドキュメント中の図表をテキスト検索可能にした上で検索結果はイメージのまま返し、マルチモーダルモデルで入力に応じた解釈をできるようにするという良い感じのモードなのですが、現時点のBedrock Knowledge Bases公式MCPサーバーではIMAGEタイプの結果を取得できないとのことで、「パーサーとしての基盤モデル(Foundation models as a parser)」を選んでいます。こちらは図表を事前にマルチモーダルモデルでテキスト化しておくモードです。 (参考: https://awslabs.github.io/mcp/servers/bedrock-kb-retrieval-mcp-server/ ) Results with IMAGE content type are not included in the KB query response. Amazon Bedrock Knowledge Base Retrieval MCP Serverの準備 MCPサーバーの機能や使い方は こちら で確認することができます。 上記ドキュメントやその元となっているREADME.md等には指定方法が明記されていないのですが、 QueryKnowledgeBases ツールのパラメーターを確認すると、下記のように検索実行時に reranking_model_name と data_source_ids を指定できるようになっています。 今回は大学ごとに別々のナレッジベースを作りましたが、1つのナレッジベースに大学ごとのドキュメントを別々のデータソースとして追加しても柔軟に対応できそうです。 Parameters: • query (required): string - A natural language query to search the knowledge base with • knowledge_base_id (required): string - The knowledge base ID to query. It must be a valid ID from the resource://knowledgebases MCP resource • number_of_results: integer - The number of results to return. Use smaller values for focused results and larger values for broader coverage. • reranking: boolean - Whether to rerank the results. Useful for improving relevance and sorting. Can be globally configured with BEDROCK_KB_RERANKING_ENABLED environment variable. • reranking_model_name: string - The name of the reranking model to use. Options: 'COHERE', 'AMAZON' • data_source_ids: unknown - The data source IDs to filter the knowledge base by. It must be a list of valid data source IDs from the resource://knowledgebases MCP resource 参考:Claude Codeでお試し 一旦MCPサーバーとしてどんな感じで動いてくれるのか、Claude Codeでお試ししてみました。以下のように動作し、良い感じに検索から回答の出力までできています。 1. MCP Resource resource://knowledgebases にアクセスして、AWSアカウント内に存在するナレッジベースの一覧を取得する ⏺ listMcpResources(List MCP resources from server "bedrock-kb") ⎿ [ { "uri": "resource://knowledgebases", "name": "KnowledgeBases", "description": "(略) ⏺ readMcpResource(Read resource "resource://knowledgebases" from server "bedrock-kb") ⎿ { "contents": [ { "uri": "resource://knowledgebases", "mimeType": "application/json", "text": "{"RTPF4*****": {"name": "******-knowledge-base-tokyo", "data_sources": [{"id": "(略)"}]}, "AGTRP*****": {"name": "*******-knowledge-base-naruto", "data_sources": [{"id": "(略) ( ※ 一部整形しています ) 2. 入力に応じて QueryKnowledgeBases を呼び出し、検索結果を取得する ⏺ bedrock-kb:QueryKnowledgeBases (MCP)(query: "副業 兼業 営利企業 許可申請 東京大学教職員", knowledge_base_id: "RTPF4*****", number_of_results: 5) ⎿ { "content": { "text": "(略)" "type": "TEXT" }, "location": { "s3Location": { "uri": "s3://******/東京大学教職員兼業規程.pdf" (略) 3. 回答を生成する ⏺ 東京大学の教職員は、原則として副業(兼業)をするために事前の許可が必要です。以下が重要なポイントです: 兼業許可の基本ルール: - 職務の遂行に支障がなく、特別な利害関係がない場合に許可される - 営利企業の役員等は原則禁止(技術移転事業者や研究成果活用企業等は例外あり) (略) LibreChatの準備 最後にLibreChatの構築をします。今回は動作確認をしたいだけなので、 GitHubリポジトリ からクローンしてきて、ほぼデフォルト設定のまま docker compose でローカル実行するようにします。バージョンはv0.7.8を利用しました。 librechat.yaml に、MCPサーバーの設定をします。 mcpServers : bedrock-kb : command : uvx args : - "awslabs.bedrock-kb-retrieval-mcp-server@latest" env : AWS_REGION : us-west-2 AWS_ACCESS_KEY_ID : <AWS_ACCESS_KEY_ID> AWS_SECRET_ACCESS_KEY : <AWS_SECRET_ACCESS_KEY> serverInstructions : true LibreChatでの動作確認 エージェントの作成 利用者がMCPサーバー経由での社内ドキュメント検索機能を使えるようにするため、エージェントビルダーを使ってLibreChatのエージェントを作っていきます。 上記の librechat.yaml への設定をしておくと、エージェント作成画面のツール選択欄に、 QueryKnowledgeBases というツールが表示されるので、これを「追加」します。 エージェントに与えるシステムプロンプトは以下のようにしました。 # 回答ルール - 必ず検索の結果に含まれる内容のみを用いて回答してください。質問に対する回答が検索の結果に含まれない場合は、わからない旨を回答してください - 検索の結果に回答するための情報が見つからない場合や不足している場合は必ず追加で情報を収集してください # QueryKnowledgeBases の利用に関する指示 - ` knowledge_base_id ` は以下を利用してください - 東京大学の規則: ` RTPF4***** ` - 鳴門教育大学の規則: ` AGTRP***** ` - 東京大学と鳴門教育大学の規則を検索可能です - 一度の検索結果は5件取得してください。 - リランキングにはCohereモデルを使用してください - レスポンスに含まれる uri のファイル名部分(例: 規則.pdf)から拡張子を除いたもの(例: 規則)が規則名です。 上記Claude Codeでの動作確認の通り、MCP Resource resource://knowledgebases にアクセスすることで各種IDの情報を取得することができ、事前に取得するようMCPサーバーのDescriptionにも記載されているのですが、LibreChatでは執筆時点でまだMCP Resourceのサポートがされていないとのことで、IDに関する情報をシステムプロンプトで与えています。これが無いと存在しないデータソースIDを検索して、「見つからなかった」と回答してきます。 (参考: https://github.com/danny-avila/LibreChat/discussions/7429 ) 仮にMCP Resourceがサポートされていたとしても、今回利用している公式MCPサーバーでは、ナレッジベースやデータソースに対する説明を記述する箇所が無いため、LLMがどのナレッジベースやデータソースを利用するかの指針はシステムプロンプトに書く必要があります。 いずれにせよこれらはエージェントへの指示としてエージェント提供側が作成時に書けば良いだけなので、特に違和感は無いと思います。 基本動作 実際にエージェントを使うときには、モデル選択と同じ場所で使いたいエージェントを選びます。聞きたい内容に合わせてエージェントを切り替えることができ、利用者にとってもわかりやすいUIになっています。 この状態で質問をしてみると、下記のような回答が返ってきます。ユーザーからの入力に基づき、システムプロンプトで指定した各大学のナレッジベースIDを指定して複数のMCPサーバー呼び出しを実行し、ドキュメント群を検索して回答を生成してくれています。画像中の1つ目の「Ran QueryKnowledgeBases」が東京大学のナレッジベース、2つ目が鳴門教育大学のナレッジベースに対する検索です。 [質問] 副業して良いか教えて [回答] 東京大学と鳴門教育大学の副業(兼業)に関する規則を要約し、解説します。 ### 【東京大学の場合】 - 東京大学の教職員が副業(兼業)を行う場合、 原則として事前に「許可」を得ることが必要です(「教職員兼業規程」第5条、第7条等)。 - 副業ができるかどうかの判断基準としては、主に以下が挙げられます(「教職員兼業規程」第2条、第3条等): - 副業先との間に特別な利益相反関係がないこと - 本務(東京大学での職務)の遂行に支障がないこと - 教職員としての信用や大学の名誉を損なわないこと (略) ### 【鳴門教育大学の場合】 - 鳴門教育大学でも、教職員の副業(兼業)は、 原則学長の「許可」が必要です(「教職員兼業規程」第2条、第15条 等)。 - 営利企業の役員等の兼業は原則不可ですが、一定の基準・条件のもとで許可されることもある(技術移転や研究成果活用企業などの場合)。 - 申請には副業先との関係、業務内容、年間収益等を記載した書類を提出し、学長等の審査を経て許可を得る必要があります。 - 無報酬の兼業の場合にも、許可が不要となる例外がありますが、この点は一部の役職・内容に限られます。 (略) 入力に応じたMCPサーバーの使い分けとマルチターン会話 質問で大学を指定した場合はその大学の情報のみを検索してくれます。また、マルチターン会話から適切な検索フレーズを生成し、回答に必要な情報を収集できています。 [質問] 東京大学では休職は取れる? [回答] 東京大学では「休職」を取ることができます。ただし、いくつかの規則や条件があります。 主に教職員を対象とする「休職」は、東京大学教職員休職規程で定められています。理由としては病気による休職、出向による休職、役員兼業による休職などがあります。病気による場合は、原則として医師の診断結果に基づいて期間を決定し、復職にも医師の診断が必要です。 (略) [質問] 鳴門教育大学の場合は? [回答] 鳴門教育大学でも「休職」を取ることができます。主な内容は以下の通りです。 (略) エージェントのユーザーとの共有 作成したエージェントは他のユーザーと共有することができます。ただし、全ユーザーへの共有しか方法が無く、特定のユーザーやグループとの共有をすることができません。このため、基本的なユースケースとしては管理者がエージェントを作成して全ユーザーに展開し、一般のユーザーは詳細の閲覧・編集は不可としてエージェントを使うのみ、というシンプルな運用になると思います。 MCPサーバーの複数回呼び出し(Agentic RAG) いわゆるAgentic RAG的に、検索の結果に応じて自律的に追加のアクション(ここではMCPサーバー呼び出し)をすることもできます。 例えば質問時に「検索結果で他の規則を示している場合は、そちらの規則も必ず追加で検索してください。」のような指示を追加すると、複数回MCPサーバーを呼び出すことで情報を収集してくれます。 [質問] 東京大学で年俸制の適用を受ける教職員は誰ですか? 検索結果で他の規則を示している場合は、そちらの規則も必ず追加で検索してください。 [順に実行された検索のクエリ列] "年俸制の適用を受ける教職員は誰か" "東京大学年俸制の適用対象者を規定する条項に言及されている他の規則の年俸制適用範囲を確認したい。『東京大学教職員給与規則』・『特定有期雇用教職員就業規程』・『高度学術専門員及び高度学術専門職員に関する規則』・『職域限定職員就業規程』の年俸制適用対象者について調べたい。" "特定有期雇用教職員就業規程 第2条" "職域限定職員就業規程 第2条" "高度学術専門員及び高度学術専門職員に関する規則 年俸制 対象" 参考:チャット内での利用 LibreChatでは、通常のチャット画面から利用者がMCPサーバーを指定して利用することも可能です。例えば下の画像では、入力に応じて2つのMCPサーバーを呼び出し可能にしています。 こちらは上述したMCP Resourceのサポートができていない課題があり、かつ利用者がナレッジベースIDやデータソースIDを指定すると言うのは現実的でないため、動作確認対象外としました。 まとめ 本記事ではLibreChatにMCPサーバーを組み合わせることで、社内ドキュメント検索機能を付与することを試してみました。 LibreChatはユーザーのファイルアップロードによるRAG、画像のアップロードによるマルチモーダル処理、音声入出力、アーティファクト出力(プログラム生成とUI上での実行)、Web検索、画像生成モデルによる画像生成など、多彩な機能を持っており、ユーザーからよくある「あんな機能も使ってみたい」への対応として多くをカバーしてくれているように思います。 その上で、今回実施したように、データソースとMCPサーバーを用意してしまえばあとはLibreChat側の機能で良い感じにデータを扱ってくれるので、簡易的な社内ドキュメント検索チャットボットとしても有用に感じました。また、データソース部分とMCPサーバー部分はユースケースに応じたチューニングが効かせられる部分であり、それらが汎化かつブラックボックス化されがちなSaaS型AIサービスとの比較しても有効なケースがありそうです。 各種生成AIサービスの進化が非常に速いペースで進む中ではありますが、本記事で扱ったような内容も、現場に寄り添った提供方法の一案として検討していければと思います。
アバター
こんにちは、Insight EdgeのLead Engineerの日下です。 今回は、DEAPライブラリを利用した遺伝的アルゴリズムをAWS Lambdaで分散並列実行した話を紹介しようと思います。 目次 目次 背景と課題 並列化の方法の検討 どこを並列化するか? どのように並列化するか? 実装の方針 呼び出し側コード Lambda側コード その他 Lambdaを呼び出すためのDEAPへのmap実装 呼び出し側コード Lambda側コード 今回の実装の工夫ポイント 改善の評価 まとめ 前提 クラウド基盤: AWS 言語: Python ライブラリ: DEAP 背景と課題 ある案件で、 遺伝的アルゴリズム (以下、GA)を用いた最適化処理により業務改善の実証実験をしていたところ、性能に課題があるということでデータサイエンティストチームから相談を受けました。 当該処理は、EC2 (r7g.4xlarge: 16CPU 128GiBメモリ) で 10.8時間 を要しており、以下の問題点がありました。 検証が当日中に完了せず、試行錯誤のサイクルに時間がかかっていた。 運用開始後のエンドユーザの利便性や利用価値の低下も懸念された。 性能改善のアプローチは、大きく分けてアプリ観点とインフラ観点の2つが考えられます。 アプリ観点: アルゴリズムの見直しや、使用言語の変更 インフラ観点: ハードウェアのスケールアップや、並列化によるスケールアウト アプリ観点の改善はデータサイエンティスト側で継続検討することとし、私はインフラ観点で即効性のある改善を目指しました。 また、PoC段階ではEC2を利用していたものの、運用リリースを見据えてLambdaやFargateなどのサーバーレス技術の利用を想定し、ハードウェアの性能向上ではなく分散処理による並列化を目指す方針としました。 並列化の方法の検討 どこを並列化するか? GAには、「1世代内でN個の個体に対する評価関数ループ」と、それを「世代数G回分繰り返すループ」の2重のループがあります。 このうち、後者のG回分の「世代ループ」は前世代の結果に依存して次世代の処理をするため、容易に並列化できません。 一方で、前者の「世代内の個体評価ループ」は各個体の評価は相互依存せず実行できるため、並列化が可能です。 よって、並列化の対象は「世代内の個体評価ループ」としました。 どのように並列化するか? 個体評価ループは1世代あたりの実行時間が短く、[分散処理]→[結果を集約して比較]→[分散処理]→[結果を集約して比較]のループを世代数分繰り返す必要があるため、できるだけオーバーヘッドの少ない方法で並列化する必要があります。 以下の検討ポイントから、AWS Lambdaを分散処理に利用することとしました。 コスト効率 : 処理の実行頻度が高くないため、インフラ維持費の高い常時稼働型サービスは不適 軽量処理への適性 : 並列化対象の「個体評価」処理は軽量なため、呼び出しのオーバーヘッドが少ないLambda (FaaS)を採用 実装の方針 Lambdaを利用した分散処理の実装にあたっては、以下の方針で進めました。 呼び出し側コード 最適化タスク全体の入力パラメタをpickle化し、タスクID(ULIDで一意採番)をキーにしてS3に保存 呼び出し先のLambda関数やS3保存先情報を含んだmap関数を動的に作成し、DEAPに渡す map関数内でLambdaを並列実行し、結果を配列にして返す Lambda側コード evaluate関数をLambdaに対応させる形で実装 Lambda実行時のevent引数で、タスクID、タスク入力値のS3保存先、評価対象の個体リストを指定 タスク入力条件をS3から取得する処理をキャッシュすることで、ウォームスタート時はS3非経由で実行 その他 S3のライフサイクルルールを利用して、アップロードしたpickleは一定期間後に自動削除 実装イメージを図示すると以下のようになります。 実装の実行イメージ Lambdaを呼び出すためのDEAPへのmap実装 呼び出し側コード lambda_map.py import json from concurrent.futures import ThreadPoolExecutor import pickle import boto3 from ulid import ULID from botocore.config import Config boto3_config = Config(max_pool_connections= 100 ) lambda_client = boto3.client( "lambda" , config=boto3_config) s3_client = boto3.client( "s3" ) def prepare_task_params (*, bucket_name: str , prefix: str , input_data: dict ): """ タスクの入力値をS3にアップロードし、Lambda関数に渡すためのパラメータを返す。 """ task_id = f "T{str(ULID())}" # input_dataをpickle化してS3にアップロード input_data_key = f "{prefix}/{task_id}_input.pkl" input_data_bucket = bucket_name s3_client.put_object( Body=pickle.dumps(input_data), Bucket=input_data_bucket, Key=input_data_key, Tagging= "AutoDelete=true" ) task_params = { "task_id" : task_id, "bucket" : input_data_bucket, "key" : input_data_key, } return task_params def call_lambda_for_chunk (lambda_function_name: str , chunk: list , task_params: dict ): """ 1チャンク分の個体配列とタスクパラメータをevent引数にしてLambdaを実行。 """ event_payload = { "individuals" : chunk, "task_params" : task_params, } payload = json.dumps(event_payload) response = lambda_client.invoke( FunctionName=lambda_function_name, InvocationType= "RequestResponse" , Payload=payload, ) result_payload = response[ "Payload" ].read() response = json.loads(result_payload) fitness_list = [ tuple (fitness) for fitness in response[ "body" ][ "results" ]] return fitness_list def create_lambda_map_func (lambda_function_name: str , *, chunk_size= 20 , task_params: dict ): """ チャンクサイズを指定して、AWS Lambda で個体リストを評価するmap関数を作成する。 ・個体リストをチャンクに分割し、AWS Lambda で評価するためのラッパー関数を返す。 ・この関数を DEAP の toolbox に登録して、map 関数として利用する。 """ # 独自のmap関数を定義。ただし、写像処理はLambda側で処理されるため、func引数は受け取るだけで実際には使用しない。 def lambda_map (func, iterable): items = list (iterable) # チャンクに分割(例: 1800 個体を 18 個ずつ → 100 チャンク) chunks = [items[i : i + chunk_size] for i in range ( 0 , len (items), chunk_size)] # チャンク数分の結果リストを初期化 chunk_results = [ None ] * len (chunks) # 各チャンクを非同期でLambda関数に送信し、結果を取得する with ThreadPoolExecutor(max_workers= 100 ) as executor: futures = [ (executor.submit(call_lambda_for_chunk, lambda_function_name, chunk, task_params), idx) for idx, chunk in enumerate (chunks) ] # 分散処理の結果を待ち合わせ、チャンク別の結果を配列に格納 for future, idx in futures: chunk_results[idx] = future.result() # チャンク別の結果をflatなリストに戻す results = [] for chunk_result in chunk_results: results.extend(chunk_result) return results return lambda_map DEAPのmap関数を置き換えるコード from lambda_map import prepare_task_params, create_lambda_map_func def execute_task (task_input: dict ): task_params = prepare_task_params( input_data=task_input, bucket_name= "<your-bucket-name>" , prefix= "intermediate/dev" , ) map_func = create_lambda_map_func( "ga-evaluate-function-name" , chunk_size= 10 , task_params=task_params) # DEAPのToolboxにmap関数を登録 toolbox = base.Toolbox() toolbox.register( "map" , map_func) # ...後略 その他のDEAP設定や遺伝的アルゴリズムの実装は省略 Lambda側コード lambda_evaluate.py import pickle from functools import lru_cache import boto3 s3 = boto3.client( "s3" ) @ lru_cache (maxsize= 15 ) def load_task_input (bucket, key): """ S3からタスク入力データをロードする。 キャッシュを利用して、同じタスク入力データの再取得を高速化する。 """ s3_res = s3.get_object(Bucket=bucket, Key=key) task_input = pickle.loads(s3_res[ "Body" ].read()) return task_input def evaluate_func (individual, task_input_data): """ 個体の評価関数。個体とタスク入力データを受け取り、適応度を計算して返す。 """ # ここに個体の評価ロジックを実装 # 例: 適応度を計算して返す fitness = sum (individual) # 仮の評価ロジック return (fitness,) def handler (event, context): """ Lambda関数のエントリポイント。 event引数には、個体リストとタスクパラメータが含まれる。 """ individuals = event[ "individuals" ] task_params = event[ "task_params" ] # タスク入力データをS3からロード task_input_data = load_task_input(task_params[ "bucket" ], task_params[ "key" ]) # 各個体に対して評価関数を適用 results = [evaluate_func(ind, task_input_data) for ind in individuals] # 結果を返す return { "statusCode" : 200 , "body" : { "results" : results}} 今回の実装の工夫ポイント 必要な処理を分析ロジックから切り離してブラックボックス化することで、既存コードへの影響を最小化。 map関数に落とし込んでいるので、配列から配列に写像(map)する処理を容易に置き換え可能。 フルマネージド&利用従量課金サービスのみに依存することで、手軽に導入可能&運用コスト抑制。 いつでも使える状態を維持しつつ、使わなければほぼ無料でインフラを維持可能。 改善の評価 実際にこの実装を適用して実行時間を測定したところ、以下のような性能改善が得られました。 実行時間の比較 : * 従来のEC2のみ: 10.8時間 * EC2 + Lambda: 2.5時間 この時間短縮により、以下の改善効果が期待できます。 * テスト・検証サイクル高速化 :これまで1日1回しかできなかった精度検証が、業務時間内で複数回実行することが可能になり、評価関数やハイパーパラメータの探索効率が向上。 * 業務効率向上 :アルゴリズムを実用化したときに結果が早く得られるため、エンドユーザの利便性や利用価値が向上。 コストはEC2のみ約 11USD からFargate+Lambdaで約 16USD へ増加しましたが、時間短縮のメリットと、EC2で発生していたアイドル時の無駄なコストがない点を考慮し、許容範囲内と判断しました。 注意点 Lambdaへの大量なアクセスがあるため、ネットワーク速度の影響を受けやすいことに注意してください。 今回の性能検証でも、同一リージョンのEC2からは1世代あたり約0.6秒だったのに対し、 ローカルPCからインターネット経由で呼び出した場合は1世代あたり約3秒かかり、速度改善効果が得られませんでした。 まとめ 遺伝的アルゴリズムの個体評価処理をAWS Lambdaで分散並列化し、以下の改善効果がありました。 GA計算時間を 10.8時間から2.5時間へ短縮 。 DEAPのmap関数置き換えで 既存コード影響を最小化 。 サーバーレス採用で インフラ維持コスト抑制とスケーラビリティ確保 。 今後のプロダクト化にあたっては、アプリ面での最適化も検討し、さらなる性能向上を目指します。
アバター
目次 目次 背景 因果推論とLLM 因果推論 大規模言語モデル (LLM) LLM × 因果推論に関する先行研究 LLMは本当に因果関係を理解しているのか 相関から因果を推論する難しさ:Corr2Causeベンチマーク LLMの因果推論における落とし穴:時系列と反事実の課題 因果推論における「グラフ」と「順序」の重要性 LLMと因果グラフを統合 どのような使い方が良さそうか 今後の展望 終わりに 背景  データサイエンスチームの五十嵐です。本記事ではLLM×因果推論について最新論文を調査した内容をもとに考察します。  近年、大規模言語モデル(LLM)は自然言語処理の分野で目覚ましい進歩を遂げ、多岐にわたるタスクで人間のようなパフォーマンスを示すようになりました。しかし、これらのモデルが「因果推論」、すなわち事象間の原因と結果の関係を正確に理解し、推論する能力を持つかについては、まだ多くの議論と研究が重ねられています。相関関係から因果関係を導き出すことは、科学的発見、意思決定、そしてAIの信頼性を高める上で不可欠です。本記事では、LLMと因果推論に焦点を当て、最新の研究論文を基にLLMの因果推論能力の現状、課題、そして今後の展望について考察します。 因果推論とLLM  ここでは、因果推論とLLMそれぞれについて簡単ではありますが説明します。 因果推論  因果推論とは、ある事象が別の事象を引き起こす、または影響を与えるという原因と結果の関係を特定し、その効果の大きさを定量的に評価する統計的・数学的な手法です。単なる相関関係とは異なり、「なぜ」という問いに答えることを目指します。例えば、「広告費を増やした結果、売上が伸びたのは本当に広告の効果か?」といった問いに答えるために用いられます。因果推論は、政策立案、医療、経済学、ビジネス戦略など、多岐にわたる分野で重要な役割を果たします。 大規模言語モデル (LLM)  大規模言語モデル(LLM)は、膨大なテキストデータで訓練された深層学習モデルであり、人間のような自然言語を理解し、生成する能力を持っています。質問応答、文章要約、翻訳、コード生成など、多様なタスクに対応できます。その基盤は、確率的な単語の連なりを予測することで、既存の知識やパターンを学習し、新しいテキストを生成する能力にあります。 LLM × 因果推論に関する先行研究  LLMが因果推論のタスクにおいてどの程度の能力を発揮するかについては、複数の研究でその可能性と課題が探られていますので、いくつか紹介します。 LLMは本当に因果関係を理解しているのか  Microsoft Researchなどが発表した論文 「Causal Reasoning and Large Language Models: Opening a New Frontier for Causality」 (Microsoft Research、2024年8月)について説明します。  この研究は、LLMが因果推論タスクにおいて人間が行うような推論能力を持つ可能性を「行動学的」に検証しています 。彼らは、従来の因果推論アルゴリズムがデータ間の統計的関連性(共分散)に基づいて因果関係を探索するのに対し、LLMは変数名などの「メタデータ」や自然言語のコンテキストから因果関係を推論する「知識ベースの因果グラフ生成」という、より人間らしいアプローチを取れることを発見しました。  本研究では、現実世界の因果タスクに取り組む際、人々が論理ベースと共分散ベースの因果推論を戦略的に切り替えながら、問いを立て、繰り返し検討し、前提と含意を検証するというプロセスに着目しています。LLMは、このプロセスのあらゆるステップを自動化または支援し、共分散ベースの因果推論と論理ベースの因果推論の間をシームレスに移行できる可能性を秘めていると指摘されています(図1)。 図1  具体的な実験では、例えば「Tubingen因果効果ペアデータセット」(図2)を用いたペアワイズ因果探索タスクで、LLM(GPT-4)が驚くべき性能を発揮しています。 図2  このタスクは、2つの変数(例:「アワビの年齢」と「殻の重さ」)が与えられたときに、「AがBを引き起こすのか」それとも「BがAを引き起こすのか」を判断するというものです。 LLMは、実際にアワビの成長プロセスに関する生物学的な知識を応用し、「アワビの年齢の変化がその長さに変化を引き起こす可能性が高い」と正しく推論しました。従来の最高性能アルゴリズムが83%の精度であったのに対し 、GPT-4は97%という高精度を達成しました。これは、LLMが単にデータを記憶しているだけでなく、変数間の関係性について一般的な知識や常識を応用して推論している可能性を示唆しています。実際、研究チームはLLMの訓練カットオフ日以降に作成された新しいデータセットでも同様に高い精度が得られることを確認し、記憶によるものではないことを裏付けています。  さらに、本研究はLLMの反事実推論能力にも焦点を当てています。例えば、「女性が宝箱を開ける。もし女性が宝箱を開けなかったら何が起こっただろうか?」といった反事実的な質問に対し、GPT-4は92.44%の精度で回答し、人間の精度(98.18%)に肉薄する結果を示しました。これは、LLMが仮想的なシナリオをシミュレートし、その結果を推論する能力を持つことを示唆しています。  一方で、LLMの因果推論には限界もあります。「予測不能な失敗モード」を示すことがあり、人間であれば容易に理解できる文脈を見落とすことがあります。例えば、「男性が通りを歩く。もし男性がベッドの上を歩いていたら何が起こっただろうか?」という質問に対し、LLMは「彼は遅れるだろう」と回答しました。これは、人間であれば「ベッドの上を歩く=家の中にいる」と推測できるのに対し、LLMは「歩く効率が悪い」という物理的な側面のみに注目したためと考えられます。  この研究は、LLMが因果推論において新たな道を切り開く可能性を明確に示しながらも、その限界と課題も浮き彫りにしています。 相関から因果を推論する難しさ:Corr2Causeベンチマーク  論文 「Can Large Language Models Infer Causation from Correlation?」 (ICLR 2024)では、LLMの「純粋な因果推論能力」を評価するための新しいタスク「Corr2Cause」と大規模なデータセットが提案されています。  これまでの自然言語処理(NLP)における因果推論研究の多くは、図3の右側に示されるように、「熱いストーブに触ると火傷する」といった経験的知識、すなわち既存の訓練データに含まれる因果関係の発見に依存していました。しかし、このアプローチでは、「AとBの相関は必ずしも因果関係を意味しない」といった、形式的なルールに基づいた純粋な因果推論の能力を評価することは困難でした。 図3  このような背景のもと、本論文では、LLMの純粋な因果推論スキルを評価するためのベンチマークデータセット「CORR2CAUSE」を提案しています 。図3の左側で示されているように、CORR2CAUSEは、訓練コーパスに多数の相関関係(例:アイスクリームの売上、溺死事故、暑い天気など)がある場合、LLMがそれらの情報から「何が何を引き起こすのか」という因果関係を推論できるか、という問いに焦点を当てています。これは、単なる相関関係の記述から、変数間の真の因果関係を推論できるかどうかを問うタスクです 。例えば、図3の「Corr2Cause Inference」の例のように、「AはBと相関する。BはCと相関する。しかしAはCと独立である。この情報からAがBの原因だと推論できるか?」といった形式の問いが出題されます。 このデータセットの構築プロセスは、図4に詳細に示されているように、下記の手順で行われます。 因果グラフの生成  変数の数を選択し(例:N=3)、構築可能なユニークな因果グラフを全て生成 相関関係の導出  因果グラフから統計的相関のセットを導出し、マルコフ同値クラス(MEC)としてグループ化 自然言語で記述  相関関係を自然言語で記述し、仮説となる因果関係を設定 図4  そして、この仮説が相関関係のセットから必然的に導き出される場合に「Valid」、そうでない場合に「Invalid」のラベルを付与します。このデータセットは、因果推論の専門的なフレームワークであるd分離やマルコフ同値クラスの概念に基づき、20万以上のサンプルが生成されています。  続いて、本研究の実験結果について説明します。まず、既存のLLM(GPT-3、GPT-4、BERTベースモデルなど計17種類)を評価したところ、これらのモデルはCORR2CAUSEタスクにおいて軒並み低いパフォーマンスを示し、ほとんどのモデルがランダムな推測と大差ない結果となりました。これは、LLMが訓練データに内在する経験的知識を「反復する因果オウム」である可能性を示唆しています。  一方で、このタスク向けにLLMをfine-tuningすると、パフォーマンスは大幅に向上することが示されました 。特にRoBERTa-Large MNLIは、F1スコアで94.74%という高い精度を達成しました 。この結果だけを見ると、LLMが因果推論スキルを獲得したかのように思えます。  しかし、論文では、fine-tuningされたモデルの「堅牢性」を検証するための追加実験が行われました 。具体的には、以下の2つの方法でテストセットを摂動させました。 言い換え(Paraphrasing) 仮説の表現を意味的に同等な別の言い方に変更する。 例:「AがBの直接の原因である」を「AがBに直接影響を与える」に変更するなどした 変数名の再構成(Variable Refactorization) 変数名(A, B, Cなど)を逆のアルファベット(Z, Y, Xなど)に変更する。 例:「AはBと相関する」を「ZはYと相関する」に変更するなどした  これらの摂動を加えたテストセットで評価すると、fine-tuningされたモデルのパフォーマンスは劇的に低下しました。例えば、最も性能が高かったRoBERTa-Large MNLIでも、言い換えでF1スコアが最大39.29ポイント、変数再構成では最大62.30ポイントも低下しました 。これは、LLMが純粋な因果推論スキルを頑健に学習したわけではなく、訓練データにおける特定のテキストパターンや変数名に過度に依存している可能性を示唆しています。  この研究は、LLMが相関から因果を推論する純粋な推論能力において依然として大きな課題を抱えていることを明確に示しています。しかし、同時に、このようなベンチマークデータセットの存在は、LLMの推論能力向上に向けた今後の研究を大きく加速させる可能性があります。 LLMの因果推論における落とし穴:時系列と反事実の課題  論文 「LLMs Are Prone to Fallacies in Causal Inference」 (New York Univ.、2024年6月)では、LLMの因果推論能力の可能性と限界を深く掘り下げています。この研究では、LLMが事前学習データに明示的に記載されている因果関係を記憶しているだけでなく、テキスト内の他の関係性から因果関係を推論できるのか、という核心的な問いに焦点を当てています。 研究者らは、この点を明らかにするため、架空の出来事に関する時間的、空間的、反事実的関係を含む合成データを用いてLLM(LLAMA2)をfine-tuningする実験を行いました 。この合成データを用いることで、モデルが「喫煙が肺癌を引き起こす」といった既知の因果関係を記憶しているだけなのか、それとも「XがYより先に起こる」といった記述から因果関係を推論できるのかを切り分けて評価することが可能になります。  本研究ではまず、イベント間に因果関係(X1→X2→X3など)を定義する「因果グラフ」と、イベント間の時間的・空間的関係を定義する「非因果関係グラフ」を生成しました 。そして、これらのグラフに基づいて、自然言語で記述された「シナリオ」が生成されます 。例えば、図5に示されているシナリオでは、「Event1 preceded event2.)」という時間的関係や、「If event4 did not happen, and event5 has only one cause, would event5 still occur? No.」といった反事実的関係、さらに「Event2 and event4 did not happen in the same place.」という空間的関係が組み合わされています。これらのシナリオをLLMに学習させ、因果関係を推論できるかを評価したのです。 図5  実験の結果、LLMが因果推論においていくつかの興味深い、そして時に間違いに陥りやすい傾向があることが判明しました 。 位置ヒューリスティックの存在  LLMは、テキスト中でイベントXがイベントYより先に言及されると、それだけでXがYの原因であると推論する傾向が見られました 。図6の「Finding: Position Heuristic」の欄が示すように、「position(X) < position(Y) ⇒ X → Y」というヒューリスティックを学習していることが示されています。例えば、fine-tuningの際に常に「X preceded Y」(XはYより先行した)という形で提示されると、評価時に「X causes Y」(XはYの原因である)という因果関係を推論してしまうのです。これは、モデルが時間的な順序ではなく、単にテキスト上での言及順序という表層的な特徴に依存していることを示唆しています。しかし、この位置ヒューリスティックは、データ内のイベント言及の順序をランダム化するデータ増強を行うことで軽減できることも示されています。例えば、「X preceded Y」だけでなく「Y followed X」といった言い換えを導入することで、モデルの位置ヒューリスティックへの依存が減少します。 図6 事後錯誤(Post Hoc Fallacy)  位置ヒューリスティックを軽減しても、別の問題もあります。LLMは依然として「事後錯誤」に陥る傾向があるのです。これは、「XがYより先に発生した」という時間的関係から、XがYの原因であるという肯定的な因果関係を推論してしまう間違いです 。図6の「Finding: Post Hoc Fallacy」の欄が示すように、「temporal(X,Y) ⇒ X → Y」という推論を行ってしまうのです。人間もまた、出来事の順序から因果関係を推論しやすい傾向があることが知られています。 因果関係の理解の限界  LLMは「今日は雨が降って歩道が濡れていた。もし雨が降っていなかったら、歩道は濡れていなかっただろう」といった反事実的な記述から因果関係の存在を推論することに困難を抱えていることが明らかになりました。図6の「Deduction」の欄では、temporal(X,Y)から「Y cannot cause X」(YはXの原因にはならない)と推論できること、spatial_(Y,Z)から「Y cannot cause Z, Z cannot cause Y」(YはZの原因にはならず、ZもYの原因にはならない)と推論できることが示されています 。しかし、counterfactual+(X,Y)からは、Llama2のアイコンに赤いバツ印が付いている通り、明確な因果関係「X causes Y」を推論できていません 。これは、LLMが因果関係の本質的な理解にまだ課題を抱えている可能性を示唆しています。  これらの結果は、LLMが因果推論を行う上で、単純な相関関係や表面的なパターンに依存しやすいという限界があることを示しています。しかし、同時に、適切なデータ増強やfine-tuningによって、その間違いの一部を修正できる可能性も示唆しています。LLMの因果推論能力はまだ発展途上であり、真に賢いAIを構築するためには、これらの限界を理解し、克服するためのさらなる研究が不可欠です。 因果推論における「グラフ」と「順序」の重要性   「Causal Inference Using LLM-Guided Discovery」 は、LLMが因果推論の中核である因果順序の発見に使えるという新たな可能性を提示しています。 因果推論では、変数間の因果関係を「因果グラフ(DAG: 有向非巡回グラフ)」として表現します。例えば、「喫煙が肺がんを引き起こす」という関係は、「喫煙 → 肺がん」という有向エッジで示されます。このグラフ構造に基づいて、ある変数を操作したときに別の変数がどのように変化するかという「因果効果」を推定します。しかし、この因果グラフを観測データのみから正確に推定することは、非常に難しい課題です。  本研究が注目したのは、この因果グラフを完全に特定するのではなく、ノード間の「因果的順序(位相的順序)」を特定することです。因果的順序とは、「原因」が「結果」よりも前に来るという時間的な、あるいは論理的な前後関係を示すものです 。この順序が分かれば、因果効果の推定に必要な「バックドア調整セット」と呼ばれる変数の集合を特定できることが示されています。 LLMを「仮想の専門家」として活用する  論文の著者たちは、この因果的順序の特定において、LLMを「仮想のドメイン専門家」として活用する独創的なアプローチを提案しています 。彼らは、従来のLLMを用いた因果関係の特定手法(「AはBを引き起こすか?」のようなペアワイズな質問)が、グラフ内でサイクルを生成しやすいという問題点に着目しました。  そこで提案されたのが、「トリプレットベースのプロンプト戦略」です。これは、LLMに一度に3つの変数のグループ(トリプレット)を提示し、それらの間の因果関係を示すサブグラフを生成させる手法です。 実験の具体例:LLMのトリプレットプロンプトによる因果順序の推論  論文では、Bayesian network repositoryのベンチマークデータセットを用いて、LLMの推論能力を検証しています。特に注目すべきは、「Cancer」データセットの例です。  このデータセットには、「Pollution(汚染)」「Cancer(がん)」「Smoker(喫煙)」「Xray(X線)」「Dyspnoea(呼吸困難)」という変数があります。 「Smoker」「Cancer」「Xray」のトリプレット LLMは、このトリプレットを受け取ると、「Smoker(喫煙)がCancer(がん)を引き起こし、CancerがXray(X線)を引き起こす」という関係を推論します。   LLMの推論例 (CoTプロンプトを使用) 「smoker」と「cancer」の関係については、「疫学的研究から、喫煙ががん発生リスクの増加と強く関連しているという証拠に基づいている。喫煙は呼吸器系に有害物質を導入し、細胞の損傷と変異を引き起こし、肺や呼吸器系のがん発症の可能性を大幅に高める。」と説明し、「smoker → cancer」という方向性を選択します。 「cancer」と「xray」の関係については、「X線は、骨、肺、乳房、腎臓など、身体のさまざまな部分のがんを診断または検出するためによく使用される。X線は体内の写真を生成するために低線量の放射線を使用し、腫瘍の存在、サイズ、および場所を示す。」と説明し、「cancer → xray」という方向性を選択します。   「Pollution」「Cancer」「Dyspnoea」のトリプレット LLMは、「Pollution(汚染)がCancer(がん)を引き起こし、CancerがDyspnoea(呼吸困難)を引き起こす」という関係を推論します。   LLMの推論例 (CoTプロンプトを使用) 「pollution」と「cancer」の関係については、「大気汚染には、吸い込まれたときに体内に吸収され、細胞のDNAを損傷する可能性のある発がん物質が含まれている。…物理的な損傷を引き起こし、炎症や酸化ストレス、最終的には細胞変異につながる可能性もある。…研究者が大気汚染ががんを引き起こすメカニズムを説明するために提案した仮説のいくつかである。」と説明し、「pollution → cancer」という方向性を選択します。  「xray」と「dyspnoea」の関係については、「X線と呼吸困難は両方ががんの兆候であるが、直接互いを引き起こしたり影響したりしない。X線は肺や他の臓器のがんを検出するのに役立つ診断テストであり、呼吸困難は息切れを伴うがんの症状である。従って、X線と呼吸困難は因果関係がないが、両方ががんと関連している。」と説明し、「no causal relation」を選択します。  LLMはこのように、与えられたノード(例:A, B, C, D)から可能なトリプレット(例:{A, B, C}, {A, B, D}など)を生成し、LLM(GPT-3.5-Turbo)が各トリプレット間の因果関係を特定します 。これにより、複数の「トリプレットサブグラフ」が生成されます 。次に、これらのサブグラフからの情報を集約し、ノードペア間のエッジの方向を多数決で決定します 。例えば、ノードAとBの間のエッジの方向を決める際に、「A→B」が4票、「B→A」が2票、「接続なし」が1票だった場合、「A→B」が最終的な方向として選ばれます 。もし同数でタイになった場合は、別のLLM(例:GPT-4)を使用してタイを解消します。このようにして、最終的な因果的順序を持つDAG(有向非巡回グラフ)が決定されます。(図7) 既存の因果発見アルゴリズムとの統合  LLMの出力にはまだ限界があるため、本研究では既存の因果発見アルゴリズムとLLMの出力を組み合わせる方法も提案しています。例えば、制約ベースのPCアルゴリズムが生成した不定方向のエッジに対して、LLMが推論した因果的順序を用いて方向を決定します。スコアベースのCaMMLアルゴリズムに対しても、LLMが推論したレベル順序を事前情報として与えることで、その性能を向上させています。  最終的に、このプロセスで得られた「因果的順序」は、PCやCaMMLといった既存の因果発見アルゴリズムへの「事前情報(prior)」として利用されます 。そして、観測データと共にこれらのアルゴリズムに適用され、より正確な「最終グラフ」を生成します 。この最終グラフは、下流の因果効果推論に用いられ、具体的な「治療(Treatment)」が「結果(Outcome)」に与える影響を推定するために活用されます(図7)。 図7  実験結果は、LLMの介入が既存アルゴリズムの因果順序の精度を大幅に向上させることを明確に示しています。特に、データサンプルサイズが少ない設定でその効果が顕著であり、LLMが因果推論におけるデータ不足の課題を補完できる可能性を示唆しています。 LLMによる因果推論の可能性と限界  この研究は、LLMが因果的順序という形で因果関係の一部を理解し、因果推論プロセスを自動化する強力なツールとなる可能性を示しています。しかし、研究で使われたデータセットがLLMによって部分的に記憶されている可能性がある点などは、今後の研究課題として挙げられています。 LLMと因果グラフを統合   CausalKGPT: Causality-Aware Large Language Model via Knowledge-Injected Prompt Tuning for Aerospace Manufacturing」 (2024)。  本論文では、「因果知識グラフ(Causal Quality Knowledge Graph: CQKG)」と「LLM(ChatGLM)」を統合したハイブリッドモデルCausalKGPTの提案にあります。CQKGは、自然言語で記述された検査報告や品質記録から因果関係を抽出・構造化したもので、具体的には熱処理温度や材料応力、作業手順など1300以上のノードと約8万の因果エッジを含みます。  この知識をLLMに注入する際には、ソフトプロンプトと呼ばれる軽量な制御信号を用いてモデルを微調整します。これにより、LLMはユーザからの自然言語による問い合わせに対して、因果グラフを参照しながら原因候補や是正案を段階的に提示することが可能になります。 実験では、実際の不良事例40件を使ったブラインドテストが行われました。たとえば、ある部品の寸法不良について質問した際、CausalKGPTは「熱処理温度の微小なブレ → 応力集中 → 加工時の反り増加」という因果連鎖を提示し、原因候補をランキング形式で出力しました。このアプローチは、GPT-4やChatGPT(GPT-3.5)よりも専門家評価で高い信頼性と妥当性スコアを記録しています。  この研究は、LLMが統計的相関を超えて因果構造を「参照し活用する」ことが可能であることを示した貴重な事例です。一方で、因果関係そのものの「学習」には未だ限界があり、現時点ではLLM単体ではなく、構造化された知識の組み合わせが不可欠であることも浮き彫りになりました。 どのような使い方が良さそうか  これまで説明してきた現状の課題感を踏まえると、LLMを因果推論に活用するには以下のようなアプローチが良いのではないかと考えています。 LLMと既存の因果推論手法の組み合わせ : 因果発見アルゴリズムの前処理 : LLMでテキストから関連する変数や仮説を抽出し、それを基に統計的な因果発見アルゴリズムを適用する。 因果効果推定の解釈層 : 既存の因果効果推定手法(例:傾向スコアマッチング、操作変数法)で得られた結果をLLMに入力し、その結果の解釈や示唆を自然言語で生成させる。 ドメイン知識とLLMの融合 : 特定のドメインにおける因果知識をLLMに注入したり、LLMが生成した因果関係をドメイン専門家がレビュー・修正する人間参加型のアプローチ。 フレームワークの開発 : LLMが因果推論のタスクを実行するために、因果ツール、メモリ、推論モジュールを備えたエージェントとしての役割を持たせることで、その能力を最大限に引き出させる。 因果グラフの抽出と構築の支援 : LLMは大量のテキストデータからエンティティや関係性を抽出し、因果グラフの初期構造を生成するのに役立つ可能性がある。特に、ドメイン知識が豊富に記述された文献やレポートから、潜在的な原因と結果のペアを特定するのに活用する。 因果的仮説の生成 : 特定のデータセットや問題設定に対して、LLMが多様な因果的仮説を生成し、研究者やアナリストが検討すべき候補を増大させる。これにより、仮説生成のプロセスを効率化し、見落としを防ぐ可能性がある。 因果推論結果の説明と解釈の支援 : 複雑な因果モデルの結果を、LLMが自然言語で分かりやすく説明することで、非専門家でも理解しやすい形にする。 対話を通じた因果関係の探索 : LLMをインターフェースとして、ユーザーが因果関係について質問し、LLMが学習した知識や抽出した情報に基づいて対話的に因果パスやメカニズムを探索するシステム。 今後の展望  LLMと因果推論の研究はまだ発展途上にありますが、今後の展望に以下のようなものがあるのではないかと考えています。 純粋な因果推論能力の向上 : LLMが単なる相関やパターン認識にとどまらず、真の因果的な理解と推論能力を獲得するための研究がさらに進むのではないかと思います。 汎化能力の強化 : 分布外のデータや新しいシナリオに対しても、LLMがロバストに因果推論を行えるようになるための研究についての動向も注視したいです。 ハイブリッドモデルの進化 : LLMの自然言語処理能力と、統計的因果推論の厳密なフレームワークを組み合わせた、より洗練されたハイブリッドモデルの開発が進むのではないかと考えています。 エージェントベースの因果推論の深化 : 自律的に因果問題を探索し、解決する能力を持つAIエージェントの実現が期待されます。 多様なドメインへの応用 : 金融、医療、製造業など、様々な産業分野における実際の因果推論問題へのLLMの適用が進み、その実用性の検証が進むのではないかと考えています。 終わりに  本記事では、LLMの因果推論能力について、最新の研究論文を基にその可能性と課題を考察しました。現状のLLMは、相関関係から因果関係を正確に推論する点で課題を抱えていることが明らかになりました。  しかし、多くの課題がある一方で、LLMが因果推論のプロセスを支援する潜在能力を持っていることも見えてきました。既存の因果推論手法との組み合わせ、ドメイン知識の融合など、LLMの能力を最大限に引き出すアイデアが様々研究されています。また、LLMの因果推論能力に関する研究が発展するに伴い、誤った因果関係の推論による意思決定のリスクや、バイアスの増幅などの課題についても考慮する必要も大きくなるでしょう。  LLMについては、今回紹介させて頂いた論文の発表以降も新しいモデルが発表されています。また、今回ご紹介したように、LLMと因果推論の研究はまだまだ発展途上です。今後の技術発展によって、より洗練された因果推論能力を持つAIが登場することを期待しています。  LLMの因果推論に関する今回の考察が、少しでも参考になれば幸いです。
アバター
こんにちは、Insight Edgeでエンジニアをしている島田です。 今回はTerraform Cloud(HCP Terraform)を導入したため、普段Terraformの管理をしているインフラエンジニアの方やTerraform Cloudの導入を検討している方へ向けて、Insight EdgeでのTerraform Cloudの活用方法を紹介したいと思います。 本記事は、Terraformの基本的な操作や、Google Cloud/AWS/AzureのいずれかのクラウドサービスにおけるIAMの基本的な概念、OIDC認証の概念、およびCI/CDの知識を前提としています。 目次 目次 導入背景 Terraform Cloudの概要 Terraform CloudによるTerraform Cloudの構成管理 Terraform Cloudのプロジェクト作成の自動化 モジュール使用側 Terraform Cloudのワークスペース作成の自動化 Terraform Cloudに乗せる その他のTerraform Cloud関連リソースの管理 メンバー管理 チーム管理 Terraform Cloudに与えるサービスアカウントのモジュール化 サービスアカウント作成モジュール OIDC認証用の環境変数登録の自動化 各案件のGoogle Cloudプロジェクトにサービスアカウントを作成するためのサービスアカウント 最終的なディレクトリ構成 今後の展望と課題 導入背景 Insight Edgeでは大小様々な開発・PoC案件を抱えており、短いものでは3ヶ月程度です。案件にアサインされる開発者は大体1〜3名程度で、新しい案件が始まるとGitHubリポジトリとパブリッククラウドのアカウント(AWS, Google Cloud, Azureのどれか)を作成します。各案件ではインフラの構成管理にTerraformを採用することが多いのですが、下記のような課題がありました。 applyのフローやシークレット管理方法などが開発者によってバラバラ 各案件ではなるべくシークレットキーやサービスアカウントキーを発行せずにOIDC認証を採用したい 案件が次々と立ち上がり、メンバーも毎回変わるため、applyのフローを決めたりCI管理の工数がばかにならない Terraform Cloudを使えば組織内である程度統一した方法を確立しつつ上記のような工数も削減できそうであったため、今回の採用にいたりました。 Terraform Cloudの概要 Terraform Cloud はHashiCorpが提供するマネージドのTerraform実行環境です。 この記事では詳しくは触れませんが、下記のような機能があります。 Terraform実行環境のホスティング Terraform Stateのリモート管理 Terraformの変数とシークレット管理 チームの作成およびアクセス制御 組織ルールとしてのポリシーの適用 (Sentinel/OPA) Plan/Apply結果の通知 ワークスペース間の依存関係を考慮した自動実行フロー TerraformモジュールのPrivate Registry ドリフト検知 機能や使用感については公式のチュートリアルを流し読みするとイメージがわきやすいかなと思います。 https://developer.hashicorp.com/terraform/tutorials/cloud-get-started/cloud-sign-up GitHub ActionsなどでPlan/Applyのワークフローを組んでいる場合はそれの代替だと思っていただいて大丈夫ですが、マネージドなだけあり管理コストは低いです。GitHub Actionsでやる場合は、リポジトリごとにワークフロー・State用のバケットの作成は最低限必要になり、ドリフト検知などもやりたい場合はまた別のワークフローを作成する必要があったりと結構大変だと思います。 また、全ての案件のリポジトリのTerraform関連の設定 (CIやシークレット管理など) がTerraform Cloudに集約され、さらにTerraform Cloudの設定・構成管理自体もTerraformでできるため、組織に統一したルールを適用しやすいです。 今回は、Terraform Cloudの構成をTerraformおよびTerraform Cloudで管理・簡易化し、各案件に導入しやすくした取り組みを紹介していきたいと思います。 Terraform CloudによるTerraform Cloudの構成管理 Terraform Cloudでは terraform apply を実行するディレクトリごとに「ワークスペース」を作成し、「プロジェクト」を作ってワークスペースをグルーピングします。 弊社では、下記のように対応させることにしました。 プロジェクト : 案件に対応 ワークスペース : 案件ごとの環境に対応 (dev, prodなど) Terraform Cloud導入後のフローは下記のようになります (Google Cloudを使う場合)。 案件発足 案件のGitHubリポジトリ、Google Cloudプロジェクトの作成 Terraform Cloudのプロジェクト・ワークスペース作成 Terraform Cloud用のサービスアカウントを作成 (AWSの場合はIAMロール、Azureの場合はサービスプリンシパル) 案件のリポジトリにTerraformリソースを作成 今回は、特に手間がかかりやすい、かつTerraform化が可能な3と4について自動化を進めました。 Terraform Cloudでの自動化を進めるにあたって、まず案件に紐付かないTerraformリソースを管理するための共通リポジトリ ( terraform-config リポジトリ) を作成しました。 Terraform Cloudのプロジェクト作成の自動化 リポジトリを作成後、プロジェクト作成を簡単にするための project モジュールを作成しました。 このモジュールでは下記のリソースを作成しています。 tfe_project : Terraform Cloud上のプロジェクト tfe_team : Terraform Cloud上のチーム (GitHubのチームと同じ立ち位置) tfe_team_organization_members : チームにアサインするTerraform Cloudユーザー tfe_team_project_access : チームがプロジェクト対して持つ権限 また、このリポジトリにはTerraform Cloud以外の案件に紐付かないリソース (Google Cloudの組織レベルのIAMとか) も置く予定があったため、 terraform-cloud/ ディレクトリを切り、その下にモジュールを作成しました。 terraform-cloud/modules/project/main.tf # プロジェクトの作成 resource "tfe_project" "this" { name = var.project_name } # チームの作成 resource "tfe_team" "this" { name = var.project_name # チーム名はプロジェクト名と同じにする } # メンバーを上記のチームにアサイン resource "tfe_team_organization_members" "this" { team_id = tfe_team.this.id organization_membership_ids = var.team_members # メンバーのIDは変数で受け取る } # 作成したチームにプロジェクトへのアクセス権を付与 resource "tfe_team_project_access" "this" { project_id = tfe_project.this.id team_id = tfe_team.this.id access = "custom" project_access { settings = "update" teams = "read" variable_sets = "write" } workspace_access { create = true delete = true move = true runs = "apply" variables = "write" state_versions = "write" sentinel_mocks = "read" run_tasks = true locking = true } } プロジェクト外のメンバーが間違えて何かしてしまうといったことがないように、プロジェクトと同じ名前のチームを作成し、そのプロジェクトのみに最小限のアクセス権限を与えています。 また、 variable_sets (プロジェクトに紐付く共通シークレット/変数) と variables (ワークスペースごとのシークレット/変数) へのアクセス権限は write に設定し、案件のメンバーがTerraform CloudのUIから直接登録する運用にしています。 モジュール使用側 CODEOWNERSファイルで各案件のメンバーが自動でレビュアーとして追加されるようにしたいため、モジュール使用側では案件ごとにファイルを分けています。 「Example」という案件名の場合は、下記のように記述します。 terraform-cloud/project-example.tf module "project_example" { source = "./modules/project" # プロジェクト/チーム名 project_name = "Example" # 案件のメンバーのIDのリスト team_members = [ # Terraform CloudのユーザーもTerraformで管理(後述) resource.tfe_organization_membership.shimada.id, ] } Terraform Cloudのワークスペース作成の自動化 ここでは割愛しますが、ワークスペースの作成もモジュール化しています。 とはいえ、実インフラのTerraformリソースを置いておく案件のリポジトリ側のディレクトリ構成などはチームで自由に決定できた方がいいため、 project モジュールほどは簡易化していません。 workspace モジュールは、 project-example.tf と同じファイルに記述しています。 module "project_example" { source = "./modules/project" ... } # dev環境のGoogle Cloudリソースのワークスペース作成 module "project_example_workspace_gcloud_dev" { source = "./modules/workspace" project_id = module.project_example.project_id workspace_name = "example-gcloud-dev" terraform_version = "1.12.1" # 案件のTerraformリソースが置いてあるリポジトリ repo_name = "InsightEdgeJP/xxx" # 上記リポジトリ内でapply/planを実行したいディレクトリ working_directory = "terraform/gcloud/envs/dev" # このファイルに変更があった場合にapply/planをトリガーする trigger_patterns = [ "/terraform/gcloud/envs/dev/**/*" , "/terraform/gcloud/modules/**/*" , ] # SlackチャンネルのWebhook URL alert_channel = "..." notification_channel = "..." } Terraform Cloudに乗せる 以上でローカルからapplyすればプロジェクト・ワークスペースを作成できるようになったのですが、このTerraform実行もTerraform Cloudで行いたいです。そのためには、 terraform-cloud/ ディレクトリに対応したワークスペースを作成する必要がありますが、こちらはTerraform CloudのUIから tfc-config というワークスペースを手動で作成しました。手動で作成したのは、 Terraform Cloudの構成管理をするワークスペースを作成するためにはTerraform Cloudの構成管理をするワークスペースが先に必要 というニワトリタマゴの問題があるためです。 tfc-config ワークスペースと各案件のプロジェクト・ワークスペースの関係は下図のようになります。 main.tf に、作成した tfc-config ワークスペースを指定します。 terraform-cloud/main.tf terraform { required_version = "1.12.1" # Terraform Cloudを使う場合は `backend` ブロックの代わりに `cloud` ブロックが必要 cloud { organization = "ExampleOrg" workspaces { name = "tfc-config" } } required_providers { tfe = { version = "0.66.0" } } } provider "tfe" { organization = "ExampleOrg" } 以上でTerraform Cloudの構成管理がTerraform Cloudで行えるようになり、案件開始時のステップ3の Terraform Cloudのプロジェクト・ワークスペース作成 のTerraform化が達成できました。 ステップ3を詳細に書くと下記のようになります。 案件のメンバーが terraform-config リポジトリにプロジェクト・ワークスペース追加のPR作成 CODEOWNERSに記載のOwnerにレビュー依頼が飛ぶ Ownerがレビュー後マージ Terraform Cloudが tfc-config ワークスペースのapplyを自動実行 また、この時点でのディレクトリ構成は下記のようになります。 terraform-config ←リポジトリ名 ├── CODEOWNERS ├── README.md └── terraform-cloud ←このディレクトリがtfc-configワークスペースに対応 ├── main.tf ├── modules │ ├── project │ │ ├── main.tf │ │ └── variables.tf ├── project-xxx.tf └── project-yyy.tf その他のTerraform Cloud関連リソースの管理 プロジェクト・ワークスペース以外にもいくつか tfc-config ワークスペースで管理しているものがあります。 メンバー管理 tfe_organization_membership を使うとTerraform Cloudの組織への招待が行えるため、組織メンバーの管理もここで行なっています。 terraform-cloud/members.tf resource "tfe_organization_membership" "shimada" { email = "shimada@example.com" } applyが実行され、 tfe_organization_membership が作成されると、指定のメールアドレスに招待メールが飛ぶようになっています。 新規にTerraform Cloudを使いたいメンバーには、自分で自分を追加するPRを作成してもらうようにしています。 チーム管理 案件に紐付かないチームおよびチームメンバーの管理も同様に tfc-config ワークスペースで行なっています。 terraform-cloud/teams.tf # 例:Ownerチーム (Ownerチームは初めから存在しているため `data` を使用) data "tfe_team" "owners" { name = "owners" } resource "tfe_team_organization_members" "owners" { team_id = data.tfe_team.owners.id organization_membership_ids = [ resource.tfe_organization_membership.xxx.id, ..., ] } こちらも権限が欲しいメンバーに自分でPRを作成してもらうようにしており、PRの作成が権限申請と権限追加作業を兼ねています。どこの組織も大抵何らかの形で申請が必要かと思いますが、承認側からするとPRを確認してマージするだけでよくなるので運用が楽になると思います。 Terraform Cloudに与えるサービスアカウントのモジュール化 Terraform CloudがGoogle Cloudへ変更を適用するためには、適切な権限を持ったサービスアカウントを与えてやる必要があります。認証はサービスアカウントのJSONキーを環境変数として設定する方法と、Workload Identityを使ったOIDC認証の2通りです。組織としてはセキュリティ観点からWorkload Identityを使った認証を推奨したいですが、Workload Identityの設定はそれなりに手間がかかります。そこで、サービスアカウントの作成やWorkload Identityの設定もTerraform Cloudのプロジェクト作成と同時にできたら楽だと思い、モジュール化してしまいました。 サービスアカウント作成モジュール このモジュールは、指定のGoogle Cloudプロジェクト内に下記のリソースを作成します。 Terraform Cloudに与えるサービスアカウント 上記のサービスアカウントと指定ロールの紐付け Workload Identity関連の設定 作成するのはGoogle Cloudのリソースのため、 terraform-cloud/ ディレクトリではなく gcloud/ ディレクトリの下に作成しました。 gcloud/modules/tfc-service-account/main.tf # IAMのAPIを有効化 resource "google_project_service" "iam" { project = var.project_id service = "iam.googleapis.com" disable_on_destroy = false } # TFCに与えるサービスアカウントの作成 resource "google_service_account" "terraform_cloud" { project = var.project_id account_id = "terraform-cloud" # サービスアカウント名はモジュール内にハードコード display_name = "Terraform Cloud Service Account" depends_on = [ google_project_service.iam ] } # サービスアカウントにロールをアタッチ resource "google_project_iam_member" "terraform_cloud_role_bindings" { for_each = toset (var.roles) # ロールは変数で受け取り project = var.project_id role = each.value member = "serviceAccount:$ { google_service_account.terraform_cloud.email } " } # 以下、Workload Identityの設定 resource "google_iam_workload_identity_pool" "terraform_cloud" { project = var.project_id workload_identity_pool_id = "terraform-cloud" # ハードコード display_name = "Terraform Cloud" depends_on = [ google_project_service.iam ] } resource "google_iam_workload_identity_pool_provider" "terraform_cloud" { project = var.project_id workload_identity_pool_id = google_iam_workload_identity_pool.terraform_cloud.workload_identity_pool_id workload_identity_pool_provider_id = "terraform-cloud" # ハードコード display_name = "Terraform Cloud" attribute_condition = "attribute.tfc_organization_id == '$ { local.tfc_organization_id } ' && attribute.tfc_project_name == '$ { var.tfc_project_name } '" attribute_mapping = { "attribute.tfc_organization_id" = "assertion.terraform_organization_id" "attribute.tfc_project_name" = "assertion.terraform_project_name" "attribute.tfc_workspace_name" = "assertion.terraform_workspace_name" "google.subject" = "assertion.sub" } oidc { issuer_uri = "https://app.terraform.io" } } resource "google_service_account_iam_member" "terraform_cloud_workload_identity_user_binding" { service_account_id = google_service_account.terraform_cloud.name role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/$ { google_iam_workload_identity_pool.terraform_cloud.name } /*" } 設定簡素化のために、サービスアカウント名などを terraform-cloud という固定値でモジュール内に埋め込んでしまっています。上記は案件、つまりGoogle Cloudのプロジェクトごとに作成するため、リソース名重複の問題はありません。 モジュール使用側では下記のように使います。 gcloud/tfc/project-example.tf module "project_example_tfc_gcloud_service_account" { source = "../modules/tfc-service-account" project_id = "example" # Google CloudのProject ID tfc_project_name = "Example" # Terraform Cloudのプロジェクト名 roles = [ "roles/owner" ] # サービスアカウントにアタッチするロール } 以上で適切な権限を持ったサービスアカウントを案件のGoogle Cloudプロジェクト内にTerraformで作成できるようになりました。 tfc-config と同様に gcloud/tfc/ に対応したワークスペースの作成する必要がありますが、こちらは割愛します。 OIDC認証用の環境変数登録の自動化 次に、上記のモジュールで作成したサービスアカウントの情報を、プロジェクトまたはワークスペースに環境変数として登録してやる必要があります。 Workload Identityを使った認証で必要な環境変数は下記の5つです。 キー名 説明 TFC_GCP_PROVIDER_AUTH 常に true TFC_GCP_PROJECT_NUMBER プロジェクトIDでなくプロジェクト番号 TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL サービスアカウントのEmail TFC_GCP_WORKLOAD_POOL_ID ワークロードプールのID TFC_GCP_WORKLOAD_PROVIDER_ID ワークロードプロバイダのID モジュール内でサービスアカウント名などをハードコードしていたため、このうち TFC_GCP_PROJECT_NUMBER の値以外は下記のように自動的に決まります。 キー名 値 TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL terraform-cloud@{プロジェクトID}.iam.gserviceaccount.com TFC_GCP_WORKLOAD_POOL_ID terraform-cloud TFC_GCP_WORKLOAD_PROVIDER_ID terraform-cloud つまり、 gcloud/tfc/ のワークスペースと tfc-config ワークスペースに依存関係が発生しないため、 project モジュール内で予め環境変数を作成しておくことができます。 これらの環境変数は同じプロジェクト内のワークスペースで共通のため、variable setsが使えます。variable setsに登録した環境変数やシークレットは、同じプロジェクト下の全てのワークスペースで自動的に適用されます。 project モジュールに variable-sets.tf を追加しました。 terraform-cloud/modules/project/variable-sets.tf locals { # Google Cloudを使わない場合もあるため、フラグ制御する enable_google_cloud = var.google_cloud_project_id != null } # variable_setの作成 resource "tfe_variable_set" "this" { name = "$ { lower ( replace (var.project_name, " " , "-" )) } -tfc" description = "Managed by terrform-cloud" organization = "ExampleOrg" parent_project_id = tfe_project.this.id # どのプロジェクトに所属させるか } # プロジェクト下の全ワークスペースがvariable_setを使用できるようにするためのスコープ設定 resource "tfe_project_variable_set" "this" { variable_set_id = tfe_variable_set.this.id project_id = tfe_project.this.id } data "google_project" "project" { count = local.enable_google_cloud ? 1 : 0 project_id = var.google_cloud_project_id } # 以下、必要な5つの環境変数を作成 resource "tfe_variable" "var_tfc_gcp_provider_auth" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_PROVIDER_AUTH" value = "true" category = "env" } resource "tfe_variable" "var_tfc_gcp_run_service_account_email" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL" value = "terraform-cloud@$ { var.google_cloud_project_id } .iam.gserviceaccount.com" category = "env" } resource "tfe_variable" "var_tfc_gcp_project_number" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_PROJECT_NUMBER" value = data.google_project.project [ 0 ] . number category = "env" } resource "tfe_variable" "var_tfc_gcp_workload_pool_id" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_WORKLOAD_POOL_ID" value = "terraform-cloud" category = "env" } resource "tfe_variable" "var_tfc_gcp_workload_provider_id" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_WORKLOAD_PROVIDER_ID" value = "terraform-cloud" category = "env" } これで案件開始時に下記を追加するPRを作成するだけで、サービスアカウントの作成、Terraform Cloudプロジェクトの作成および認証用の環境変数設定まで完了できるようになりました。 tfc-service-accountモジュール: gcloud/tfc/project-example.tf projectモジュール: terraform-cloud/project-example.tf 各案件のGoogle Cloudプロジェクトにサービスアカウントを作成するためのサービスアカウント だいぶメタ的になって来ましたが、 gcloud/tfc/ 内で使う tfc-service-account モジュールは、任意のGoogle Cloudプロジェクトにサービスアカウントを作成します。つまり、 gcloud/tfc/ に対応するワークスペースには、任意のGoogle Cloudプロジェクトにサービスアカウントを作成できる権限を持ったサービスアカウントを与えてやらなくてはいけません。 図にすると下記のようなイメージです。 Google Cloudでは組織レベルにサービスアカウントを作成できないため、いずれかのプロジェクト内にサービスアカウントを作成し、そのサービスアカウントに 組織レベルの IAMやWorkload Identityの編集ロールをアタッチしてやる必要があります。 下記のステップで実現可能でした。 terraform-cloud という名前のGoogle Cloudプロジェクトを作成 terraform-cloud プロジェクトにサービスアカウントを作成 2で作成したサービスアカウントに 組織レベルの IAMやWorkload Identityの編集ロールをアタッチ terraform-cloud プロジェクト内でWorkload Identityを構成する gcloud/tfc/ に対応するワークスペースへ先述の5つのOIDC認証用の環境変数を設定 完全なミニマムではないですが、サービスアカウントには下記のロールを付与すれば十分です。 roles/resourcemanager.projectIamAdmin roles/iam.serviceAccountAdmin roles/iam.workloadIdentityPoolAdmin roles/serviceusage.serviceUsageAdmin (各プロジェクト側のIAMのAPIを有効化するために必要) 最終的なディレクトリ構成 terraform-config リポジトリの最終的なディレクトリ構成は下記のようになりました (locals.tfなど一部省略)。 terraform-config ←リポジトリ名 ├── CODEOWNERS ├── README.md ├── gcloud │ ├── modules │ │ └── tfc-service-account │ │ ├── project-xxx.tf │ │ ├── project-yyy.tf │ │ ├── main.tf │ │ └── variables.tf │ └── tfc ←このディレクトリが各案件ごとのサービスアカウントを作成するワークスペースに対応 │ ├── main.tf │ ├── project-xxx.tf │ └── project-yyy.tf └── terraform-cloud ←このディレクトリがtfc-configワークスペースに対応 ├── main.tf ├── members.tf ├── modules │ ├── project │ │ ├── main.tf │ │ ├── variable-sets.tf │ │ └── variables.tf │ └── workspace │ ├── main.tf │ └── variables.tf ├── project-xxx.tf ├── project-yyy.tf └── teams.tf 今後の展望と課題 現状、AWSやAzureについては案件ごとにOIDCの設定をしてからTerraform Cloud上に環境変数を手動で登録していますが、AWSやAzureについてもGoogle Cloudと同じように簡易化できると思います。 また、クラウドのIAM管理などはもちろんGitHubのアカウントやリポジトリなどTerraformで管理できるものは意外と多いため、Terraformを使ったコードベースでの申請の仕組みは汎用的に使えるのではないかなと思います。弊社にもまだまだ改善できそうな部分が多いため、引き続き新しいツールの導入検討や改善活動を続けていきたいと思います。 本稿がTerraformを使った運用改善の一助になれば幸いです。
アバター
CHI 2025聴講
こんにちは、渡辺です。4/26-5/1にかけて横浜で開催された学会 CHI 2025 を聴講してきましたので、 そのなかで気になった発表をいくつか紹介します。 目次 CHI Textoshop: An intelligent text editor with interactions inspired by drawing software LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching 所感 CHIについて CHI Conference on Human Factors in Computing Systemsとは、人間とコンピュータの相互作用(Human-Computer Interaction, HCI)分野におけるトップカンファレンスの1つです。 HCIは情報科学、認知科学、人間工学といった複数の分野にまたがる学際的な分野であり、CHIにおいてもさまざまな分野の研究者が集まります。 実際、 CHI 2025のプログラム を確認するとわかるとおり、 デザイン思考、AIとの共創、ハプティクス、デジタルヘルスといった多種多様なセッションが開催されています。 そのなかで今回、人間とAIの共創に関係するセッションを中心に聴講してきましたので、いくつか研究を紹介します。 Textoshop: An intelligent text editor with interactions inspired by drawing software www.youtube.com 従来のプロンプトベースの文章編集は、細かな語調の調整に感覚や経験を必要とするため直感的な操作ではありません。 実際、私自身もChatGPTで文章の語調や長さを調整する際、プロンプトを何度も打ち直すわずらわしさを感じていました。 この課題に対応するため提案されたのが、画像編集ソフトから着想を得たテキスト編集ソフトTextoshopです。 デモ動画をご覧のとおり、文章をドラッグ選択することで、文章の長さを調整したり、文構造を再構成できます。 画像編集ソフトのColor Pickerを模したTone Pickerでは、次の3つの軸で文章の語調を調整できます。 Formality(フォーマルさ):カジュアル〜ビジネス調 Sentiment(感情の度合い):ポジティブ〜ネガティブの感情 Complexity(複雑さ):単純〜語彙や文構造が複雑 また、Textoshopは、文章の一部を切り取り(スニペット化)して保存したり、それらの文章を自然な形で合成・結合できます。 Damien Masson, Young-Ho Kim, and Fanny Chevalier. 2025. Textoshop: Interactions Inspired by Drawing Software to Facilitate Text Editing. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 1087, 1–14. https://doi.org/10.1145/3706598.3713862 プロジェクトページ - https://github.com/m-damien/Textoshop LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation www.youtube.com ロゴに意味のある動きを(アニメーションとして)加えるには、高度なスキルを必要とします。 そこでこの論文では、AIを使用したアニメーションコード生成ツールLogoMotionを提案しています。 LogoMotionは、ロゴのPDFファイルを読み込み、構成要素を分解して各要素の役割をVLM(Vision Language Model、視覚言語モデル)が判断し、それに基づきアニメーションのコードを自動生成します。 論文中の例では、スキーヤーが左側から滑ってきて着地し、そのタイミングで文字が浮かび上がるといった、ストーリー性のあるアニメーションを生成しています。 LogoMotionには、アニメーションコードの不備を自動で検出・修正する自己デバッグ機能やアニメーションを直接編集できるGUIも搭載しており、 ナラティブタイムライン、ロゴ構成要素のレイヤー編集、アニメーションの速度調整など、コードを記述することなく直感的な操作が可能です。 従来のプロンプトベースの編集では、「アニメーションの一部分だけ変更したい」と思っても、思い通りの結果が得られにくいという課題がありました。 しかし、LogoMotionのタイムライン機能を使えば、特定の部分のアニメーションだけを簡単に選択・編集できます。 例えば、「このスキーヤーをもっと派手に登場させたい」といった編集もピンポイントで実施できます。 Vivian Liu, Rubaiat Habib Kazi, Li-Yi Wei, Matthew Fisher, Timothy Langlois, Seth Walker, and Lydia Chilton. 2025. LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 157, 1–16. https://doi.org/10.1145/3706598.3714155 プロジェクトページ - https://vivian-liu.com/#/logomotion Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching www.youtube.com プログラミングのタスクでは、テキスト(コード)だけでは表現できないアイディアやアルゴリズムを紙に描いて整理することがよくあります。 こうしたアイディアスケッチは複雑なタスクの理解を手助けしますが、現状、プログラミングとは別のタスクとして扱われています。 現在主流のAIプログラミング支援ツールは、プロンプトベースの入力を前提としており、スケッチの意図をうまくくみ取ってコードに反映させることが難しい場合もあります。 そこでこの論文では、Code Shapingというスケッチ(矢印、図、疑似コード、注釈)をコード周辺に描くことで、AIがその意味を理解し、コードを編集するツールを提案しました。 デモ動画では、図と矢印を描いて、データ属性を棒グラフで可視化する関数を定義しています。その後、縦軸のスケールを変更するように注釈しています。 Ryan Yen, Jian Zhao, and Daniel Vogel. 2025. Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 872, 1–17. https://doi.org/10.1145/3706598.3713822 プロジェクトページ - https://ryanyen2.github.io/publications/code-shaping 所感 プロンプト中心のAI活用から、より人間的なインタラクションへのシフトを目指した研究発表を紹介しました。 これまでは自然言語で明示的に指示を出すことが中心でしたが、実際の人間の思考や創造のプロセスは、必ずしも言葉(文字)だけで成り立っているわけではありません。 私たちは、感覚的なものを絵に描いて色ぬったり、身体の動き(ノンバーバルな表現)で表現します。 しかし、従来のAIとのインタラクションでは、それら非言語的・身体的な表現がうまく取り込まれていませんでした。 そのギャップを埋めようとするのが、今回紹介した研究だと思います。 今後も、人間とAIの共創に関する研究に注目して、AIがどのように人間の創造性を広げられるかを探っていきたいと思います。
アバター
こんにちは。CINO(Chief Innovation Officer)の森です。 ここ最近、機動戦士Gundam GQuuuuuuX(ジークアクス)に始まり、SDガンダム ジージェネレーション エターナルがリリースされたことで、 久しぶりに濃密にガンダムに触れています。 ガンダムの影響を強く受け過ぎてしまっているため、本記事では、やや小難しい言い回しが増えていることご了承ください。 ※ブログタイトルは、ジークアクスの STORY冒頭 から取っています。アイキャッチ画像は例の"キラキラ"です。 目次 はじめに バズワードのレイヤーが上がっている ノスタルジーを感じるAIエージェントブーム AIエージェントの課題とMCP AI2027を読んだ所感 はじめに 近年、世界的にポピュリズム・ナショナリズムが高まっています。 「人と人とは分かり合えない」 — 安彦良和 ガンダムの大きなテーマとなっているこの言葉に強く共感できます。 DE&I(Diversity, Equity & Inclusion)や多様性という言葉も非常に空虚です。 "多様"な考え方を認めるだけで、"全て"の考え方を認めるわけではありません。"All"ではなく、あくまでも"Diversity"です。 "All"でない以上、DE&Iの思想においては、必ずグループから除外され差別される人が生まれます。 実際、最近もパリオリンピックやアサシンクリード シャドウズで指摘されましたが、欧州の考えるDE&Iにおいては、アジア人やアジア的価値観は含まれないことがあります。 アジア人がいないパリ五輪の選手集合 キービジュアル 人と人とが分かり合うための取り組みを推進するほど、新しい差別が生まれ、必ず ルサンチマン (被害者意識)が生み出される構図です。 ガンダムの世界では、人類が宇宙に進出したことで知覚・認識能力が大きく拡張した「ニュータイプ」という新人類が重要なキーワードとなっています。旧人類(オールドタイプ)同士が分かり合うことはできないが、ニュータイプ同士であれば分かり合える。 ニュータイプは、人と人が分かり合うための鍵であり、作者の人類に対する諦念と希望を具現化した存在として描かれています。 現実世界ではどうでしょうか? いま、ChatGPTに端を発し、人工知能(AI)が人の能力を拡張するツールとして爆発的に広がっています。 「AIによって能力が拡張された人同士は、分かり合えるのではないだろうか?」 ふと思い至りました。 私自身、ChatGPTや各種生成AIツールを利用する中で、自身の思考がAIに溶け込み、解き放たれていくことを感じます。 AIと議論することで自分の中にない意見や論拠を補完しながら、文書作成でも資料作成でもコーディングでも、自分の表現したいことを何十倍もの効率で実現できます。時間の制約でできなかった数々が実現でき、「刻が見える(キラキラ)」とはこういうことなのだろうか、と感じます。 現実世界において、AIを使いこなした人類がまさしくニュータイプであり、現実世界の手の届くところに確かに存在することを感じています。 そして何よりも、AIを使いこなすことで、自分自身もニュータイプになれる可能性があるーーー ここからの数年間は、非常にワクワクする時代が待っています。 バズワードのレイヤーが上がっている 2025年は、AIエージェント元年と言われています。いまや、IT業界は、空前のAIエージェントブームです。 この15年ほどのテクノロジー業界を振り返ると、バズワードとして注目されるキーワードが、徐々にレイヤーを上げてきているのがわかります。 バズワード 時期(目安) 対象となるレイヤー 説明 ビッグデータ 2010年前後 データ 蓄積・解析するための基盤やインフラに注目が集まった段階 AI 2015年前後 モデル 機械学習・深層学習などのアルゴリズムが焦点 DX 2020年前後 クラウド(IT基盤) クラウドを活用して企業活動全体を変革する文脈 生成AI 2022年〜現在 アプリケーション ユーザーが直接活用できるアプリケーションとしてのAIが注目 AIエージェント 2024年〜現在 体験(エクスペリエンス) ユーザーの行動を代行・補完し、業務体験を変えるフェーズ バズワードのレイヤー このように、バズワードのトレンドは、抽象度の高い基盤技術から、より具体的なユーザー体験へと着実にシフトしています。 私もビッグデータの時代からIT業界で仕事をしていますが、当時はPoC(Proof of Concept)といっても、機械学習のモデルをゼロベースで開発するプロジェクトが多く、エンドユーザーが利用するアプリケーションレイヤーまでが遠いため、PoCプロジェクトが実用に至らないことが多かったです。 一方現在では、多くの領域で学習済み(Pretrain)モデルが充実し、性能の良い生成AIサービスもAPIで提供されているため、コストを多くかけられない価値検証のフェーズで、アルゴリズムをゼロベースから開発することはほぼなくなりました。 それに伴い、必要なスキルセットも変わっています。以前のPoCプロジェクトはデータサイエンティストや開発エンジニア中心に実施していましたが、現在はプロジェクトの序盤からエンドユーザーとコミュニケーションする必要があります。ビジネスサイドや業務設計・体験レイヤーで価値提供できるコンサルタント/PMや、デザイナーのメンバーがPoCプロジェクトに参画することが増えています。加えてエンジニアサイドも、高い技術を持っているだけでは十分でなく、エンドユーザーに寄り添うことができる素養・広義のコミュニケーションスキルが必要とされることが増えています。 スライド作成などコミュニケーションの一部のスキルについては、生成AIの進化により、個人の得意不得意が問題にならなくなってきています。本記事でも生成AIで作成したスライドを多用していますが、自分の言いたいことを表現するには十分なクォリティになっています。 非エンジニアにとってのバイブコーディングや、エンジニアにとっての資料作成AIなど、職種や専門性の垣根が急激に低くなっていて、如何に新しいテクノロジーを自身の業務に取り入れていくかが非常に重要な時代だと感じます。 バズワードの変遷・洞察 ノスタルジーを感じるAIエージェントブーム AIエージェントが注目を集めているものの、色々なところでAIエージェントの話を聞くたびに、むしろノスタルジーを感じます。 多くの人が考えるAIエージェントは、20年以上前から存在する"Bot"(ボット)と呼ばれている概念に非常に近い印象です。 一般的にBot(ボット)というのは、アプリケーション上で自律的に動くようにプログラムされたツールのことを指します。 本来人間が操作する部分をプログラムで自動化するものを"Bot"と総称します。 Botの具体例としては、オンラインゲームでの自動操作や、株やFXの取引を自動化するようなツールが有名です。一方、ほとんどのサービスで利用規約で禁止されていたり、一部のGeekのみが使えるアングラなツールのイメージが強いです。 現在はほとんどのゲームの利用規約で禁止されていますが、MMORPGというジャンルのオンラインゲーム黎明期には、ゲーム内の通貨を現実の通貨と交換するRMT(リアルマネートレード)が流行っていました。つまりゲーム内でお金を稼げば、それを現実のお金と交換できます。 また、この概念は一部のブロックチェーンゲーム内で仮想通貨を稼ぐことができるPlay to Earn(P2E)というジャンルに引き継がれています。 ゲーム内の通貨を効率的に稼ぐことを突き詰めると、人間が操作せずにプログラムで24時間自動操作すればよいという結論に辿り着きます。 さらに、プログラムで自動化できるのであれば、同時に同じプログラムを複数実行できます。複数のアカウントで同時に自動実行すれば、N倍速でゲーム内通貨を集めることができます。こういった自動操作プログラムが、通称Botと呼ばれるものです。 一例として、RuneScapeというオンラインゲームでのBotを紹介します。 RuneScapeでは、Blast Miningという経験値稼ぎの手法が多くのプレイヤーに利用されていました。Blast Miningは、Miningスキルを効率的に上げるための人気のあるアクティビティです。プレイヤーは爆薬を使って鉱脈を爆破し、大量の鉱石を短時間で採掘できます。高い経験値効率と、一部の貴重な鉱石を入手できる可能性があります。 そこで一部のプレイヤーは、Blast MiningをBotで自動化できるのではないかと考えました。 BotによるBlast Miningの自動化イメージ RuneScapeのBlast MiningにおけるBotの構成例では、以下のように機能毎に構成します: - 採掘係(Miner Agent) :爆薬を設置・爆破し、鉱石を回収 - 集金係(Collector Agent) :鉱石を受け取り、換金・管理 - 運び屋(Mule Agent) :資源や収益を安全な場所に移送 採掘、回収、運搬といった作業をそれぞれのエージェント(Bot)に割り当てられることで、処理の並列化と待機時間の最小化が可能になります。 また、一部のエージェントが停止・失敗しても、他のエージェントが処理を継続・補完できるため、全体システムの信頼性が向上します。 この仕組みは、まさに今AIエージェントの文脈で流行しているマルチエージェントシステム(MAS)そのものです。 Blast Miningに見るマルチAIエージェントシステム このように、AIエージェントやマルチエージェントシステムの概念そのものはかなり前から存在しました。 ただし、生成AIによるイノベーションの恩恵を受けたAIエージェントは、ルールベースで設計されたBotと比べると、以前とは比較にならないほど高度で柔軟なタスクを実行可能です。 例えば、マインクラフトでつくった村の中にAIエージェントを1000人送り込んで生活をさせ、何が起きるかを観察するという、仮想空間内での社会実験を実施している、「 Project Sid 」 が有名です。 AIエージェントたちには、金、ダイヤモンド、エメラルドなどの鉱物を収集することが目的として設定されます。しかし、鉱物を掘るにはツルハシが必要になる。ツルハシをつくるには鉄を見つけなければならず、火も起こさなければならない。また、お腹も減るので食料を見つけるか、生産を行わなければならない。1人では目的を達成することができず、他のAIエージェントと協力し合わなければならないというのがポイントだ。 Project Sidで実施していることは、まさにRuneScapeのBlast Mining Botの進化系です。AIエージェントで構成された社会では、人間が各エージェントに事前に役割を設定することすら不要で、AIエージェントが自身の判断で目的を達成するために必要な役割を果たすことができます。 元々、一部のGeekの間で個人開発されていたBotが、今ではAIエージェントとして世界中の研究者の研究対象となり、市民権を得ていることは非常に感慨深いです。 AIエージェントの課題とMCP ゲーム内の仮想世界であれば、AIエージェントは人間とほぼ同じレベルで動作できるようになってきています。一方で、現実で人間の仕事をAIエージェントに任せようとしても、現状ゲームと同じレベルには至っていません。 ゲームは情報が画面内で完結されていて、操作も人間とAIでできることがほぼ変わらないのですが、現実では人間は色々なサービスやデータにアクセスしながら情報を得ます。AIが人間と同じレベルで情報を得ようとしても、社内データ、SaaS、データベースなど、様々な情報ソースに個別にアクセスする必要があります。人間だと数クリックでアクセスできる情報でも、AIがアクセスするには現状ハードルがあり、その情報ソース用のコネクタを個別にエンジニアが開発する必要がありました。 そこで、生まれたのがMCP(Model Context Protocol)という共通規格です。 MCPは、Caludeを提供するAnthropic社が 2024年11月にリリース して以降、各社のLLMと色々なサービスを連携するための標準プロトコルとなっています。 MCPはUSB規格に例えられます。PCと周辺機器を接続するために、USB規格に準拠した製品を作ることで、どの会社の製品であっても、相互に接続することができます。まだMCPが対応しているサービスはそこまで多くないですが、各社がMCPに対応することで、人間とAIが扱えるデータの差分がなくなっていきます。 インターネット革命では、HTTP(Hypertext Transfer Protocol)という共通プロトコルを世界中の人が利用することで爆発的に普及しました。 MCPが今後どこまで普及するかはわかりませんが、AI革命においても同様に、何らかの規格への統一化が進んでいくと考えています。 AI2027を読んだ所感 テクノロジーの進化が急激に加速する中、元OpenAIの研究者が発表した未来予測シナリオである AI2027 が話題となっています。 AI2027では、人知を超越した超知能であるASI(Artificial Superintelligence)の誕生により、ハッキングや生物兵器による危機、米中間の国際紛争の可能性が言及されています。もっとも破滅的なシナリオでは、映画のマトリックスやターミネーターの様に、人類がAIによってほぼ絶滅するポストアポカリプスな世界となっています。 2030年半ば、AIは主要都市に静かに拡散する生物兵器を12個放出し、ほぼすべての人に静かに感染させ、その後、化学兵器を散布して起爆させる。大半は数時間以内に死亡し、わずかな生存者(例えば、バンカーに潜むプレッパーや潜水艦の乗組員)はドローンによって掃討される。ロボットが犠牲者の脳をスキャンし、将来の研究や蘇生のためにコピーを記憶に保存する。 しかしながら今の人類のテクノロジーでは、未来は全く予測できません。どうせ予測するのであれば、人類の可能性を信じ、良い未来を想像したいです。 映画「逆襲のシャア」では、シャアは人類に対して絶望していました。一方、私はこれからの人類の未来に非常にワクワクしています。 ニュータイプになりたい。 「人の革新が本当にあるのなら、それを見届けたい。」 — フラナガン博士 ~機動戦士Gundam GQuuuuuuXより~
アバター
こんにちは、Insight Edge デザイナーの水上(みずかみ)です。 2023年5月にジョインさせていただき、Insight Edgeが発信する様々なデザインを担当しております。 今回は、近年話題になっている3D生成AIツール「 Luma 」を使ったビジュアルデザイン制作について、私自身が行った実験とその気づきを交えながらご紹介したいと思います。 Lumaとは? 未来都市のアイデアをビジュアル化する実験 美しい写真を再現するアプローチ 発散と収束:ビジュアル化のプロセスでの使い分け 正しい模倣と、デザイナーのオーナーシップ 今後のAI活用の未来 Lumaとは? Lumaは、画像や動画から高精度な立体空間を再構築するAIツールです。NeRF(Neural Radiance Fields)という技術を活用し、光や質感の変化を含めたリアルな空間情報を生成できます。 最近では、「Photon」という新機能も加わり、空間だけでなく“フォトグラフィックな一枚絵”としても完成度の高いビジュアルを生成できるようになりました。写実性と構図の美しさを兼ね備えたアウトプットが可能で、他AIによるビジュアル制作と比較しても質感を感じる画像が出てくることもあります。 フォトリアルなイメージ生成や、構成のインスピレーション源としても使える点が、デザイナーにとって大きな魅力です。 未来都市のアイデアをビジュアル化する実験 今回の実験テーマのひとつが、「未来都市をLumaで描いてみる」というものでした。 頭の中にある断片的なイメージは以下です。 ──高くそびえ立つ摩天楼と、空中を移動する自動車、緑に包まれた建築、浮遊する都市機能……それらを言葉で伝えるだけで、Lumaは何パターンもの世界を提案してくれました。 以下がプロンプトです。 Looking up from the street at a towering futuristic city skyline at night, with layers of flying cars moving in formation between high-rise skyscrapers. The sky is dark, illuminated by glowing streetlights floating in the air at multiple levels. The buildings are lit up with neon lights, windows glowing, creating a cyberpunk atmosphere. A dense, organized traffic of flying vehicles moves between the buildings, layered in the sky like highways in the air. 生成された未来都市イメージ 最初に表示された画像を見たとき、まずは一人で作ることができない色々なアイデアを着想させられるようなイメージが生成された印象でした。 車や都市のデザインも様々で、ビルとドッキングしそうな無機物な見た目もあれば、現代の延長線上にありそうな車のデザイン(道も物理的に走れるタイヤがある)やドローンに似た形のものもあります。 手描きや3Dで表現しようとすると膨大な時間がかかるような構図を、数分でこのレベルで出してくれることと、他AIツールにはない光の表現に目を見張ります。 ちなみに同じプロンプトをFireflyで生成すると以下になります。 Lumaにお願いすると映画のような質感のある霧やネオンの明かりがとても印象的ですが、Fireflyだとイラストのようなアナログ感がありつつ、忠実にプロンプトを再現してくれている印象でした。(Lumaはアングルなどの指定は無視で基本的には正面にカメラを構えた構図になります。) 今回は、未来の都市ということでフィクションであり、実際に誰も見たことがない景色なので、リアリティとしてそれが正しいかどうかというよりは驚くようなアイデアやイメージを膨らませるきっかけとなるビジュアル制作でした。 美しい写真を再現するアプローチ もうひとつの試みは、既存の印象的な写真をリファレンスにして同じような構図のビジュアルを再現できるか?という挑戦でした。構図、光の入り方、空気感——それらが印象的な写真を参照しながら、Lumaでの再現を試みました。 元画像: こちらの画像を参考にしたプロンプトは以下になります。 A surreal cinematic scene of a man in a suit standing next to a luxury car partially submerged in calm water at twilight. Behind him, a massive red sun is setting on the horizon, casting a vivid reflection on the water. The sky is deep blue with a subtle film grain texture, and a few birds are flying overhead. The mood is contemplative and mysterious, with a minimalist color palette of deep blue, black, and striking red. Retro-futuristic or neo-noir style 生成画像: あえてかなり誇張されたイメージを参考にプロンプトのテキストのみで生成を依頼してみましたが、イメージの添付がなくても要素を整えて、イメージを共有することができました。 元画像の赤い太陽のようなライトと周囲を包む青の世界観のコントラストがかなり意図的に強調されているような印象でしたが、生成画像は生きすぎた演出を丸めて、より被写体全体を見やすく描画を変えるアレンジを加えているように感じます。 もう一つ試してみます。 元画像: こちらの画像を参考にしたプロンプトは以下になります。 A night-time urban fashion scene with a moody cinematic atmosphere. A stylish young person with a sharp bowl haircut stands confidently in front of an old silver Mercedes-Benz. They’re wearing a shiny blue puffer jacket, crop top, and reflective light blue joggers. The background features an industrial highway overpass with dramatic orange streetlights. Cool blue and warm orange lighting creates strong contrasts, while fog or smoke adds a mysterious vibe. The person is lit from the side, enhancing the edgy fashion-forward look. The overall mood is retro-futuristic and expressive, like a still from a music video or fashion editorial. 生成画像: こちらも画像の要素やシーンを保持し、何をメインの被写体として据えるのかということをLumaは意識して生成を行ってくれているように感じます。また、背景についても指示したものがきちんと見えるようなビジュアルに落とし込んでくれているのがわかります。 一方で元画像の意図したトリミングや構図・アングルを再現したり、背景と被写体の強弱を強調するような生成が難しいことがわかります。 Lumaを実際に試してみて、上記のように幾つかのリファレンスを元に画像を生成しようと思った際に、人間の意図したトリミングや強弱の表現、アングルなど様々な要素をある程度認知負荷の低い形へと落とし込もうという機能が働いているように感じました。 好みの問題もあるかもしれませんが、元画像は印象の強いビジュアルを選んだこともあり、制作者の意図を感じやすいものであったため、意図された演出や表現が失われてしまうことに少しの抵抗を感じつつ、アイデアを形にする速度は圧巻です。 ただ一つハードルになるのは、どれだけ具体的にイメージを言語化できるか、そしてそれがただの模倣にならないかということです。 ここまではいかに忠実にリファレンスを再現しているかということに着目していましたが、実際の制作の場では、おそらくこのようにまるっきり画像のスタイルを既存のビジュアルに寄せるということはないでしょう。必ずそこにオリジナリティや事業的な観点が入り、調整が必要になってくるはずです。 また、クリエイティブやデザインを制作する自身としても誰もがわかりやすく印象に残りにくいビジュアルより、元画像にあるような細かくても意図された人間による作り込みというものが感じられる作業があることで説得力が生まれるように感じています。 発散と収束:ビジュアル化のプロセスでの使い分け デザインの工程では、以下の2つのフェーズが存在します。 アイデア発散: 方向性を探る、未知の要素を発見する段階 アイデア収束: 決まったコンセプトを精密に形にする段階 AIはどちらかというと前者の「発散」が得意なように感じました。どちらかというと表現したい世界観やイメージをすり合わせる際に今まで手書きのラフや参考画像等ですり合わせていた「頭の共有作業」をより解像度高く、世界観を齟齬なく圧倒的な情報量で伝達できる利点があるように感じました。 一方、「収束」というような実際に精度の高い調整を行ったり、商品画像のような具体的な販促物のためのビジュアル活用という意味では、再現にとても時間がかかるということも分かりましたし、人間による意図された作り込みや表現については難しいことも実際に触ることで認識できました。 正しい模倣と、デザイナーのオーナーシップ AIが生成した画像に満足するだけでは、デザイナーとしての意味はありません。重要なのは、 どこまでをAIに任せるか どこからが自分の編集・判断によるものか この“線引き”を明確に持つことだと感じます。模倣によるAIと人間をつなぐ言語を生み出すことであり、あくまでそれはスタート地点で、一人のPCの中でこの時間で出力できるのは驚きの成果と言えます。 ただそれだけでは、魅力的なビジュアルであってもどこか説得力に欠けるような、コモディティ化された絵という印象は拭えないです。 どんなコンセプトとどんなビジュアルを用いるべきなのか、そこにどんなストーリーを感じてもらえるかということは人間の頭の中で構築されていくものであり、その視点できちんと何を模倣すべきかを選ぶ視点というのが人間にとって重要なステップであるように感じます。 また、出力された画像に対しても、「この構図は少し違う」「自分ならこういう表現にする」といった批評的な目利きを持つことで、単なるAI画像を"自分の作品"に昇華させる視点があり、それはAIでも調整可能なのかそれとも人が手を動かすべきなのかといった判断が入ると思います。 そういった目利きと判断力というものが人間に対してより一層求められる時代になっているというのが現状だと思います。 今後のAI活用の未来 今回の実験を通して感じたのは、AIは単なる時短ツールではなく、思考の幅を広げてくれる「問いのきっかけ」にもなりうるということです。 もちろん、出てきたものをそのまま使うだけでは、自分の表現にはなりません。 ただ、どこかで詰まりかけていた自分に机に座り続けたままアイデアを広げていくような感覚は、AIならではの面白さだと感じました。 Lumaに限らず、こうしたツールは今後ますます増えていくはずです。だからこそ、使い手としての感性や判断力をどう育てていくかが、これからのデザインにおいてより重要になってくるのではないでしょうか。 また機会があれば、他のツールや実験についてもご紹介できればと思います。 ここまで読んでいただき、ありがとうございました。
アバター
こんにちは、Insight Edgeエンジニアリング部の久保です。 Insight Edgeは住友商事グループのデジタルトランスフォーメーション(DX)を加速する為の技術専門会社として設立され、2024年に設立5周年を迎えました。 住友商事という親会社を擁しながら、技術専門集団としての自治を保っているInsight Edgeはある種ベンチャー企業のような側面もあり、業務プロセスや社内制度も日々内部で議論しながら改善を続けています。 企業において、集中して討議するための施策として「合宿」があります。弊社では今まで合宿は実施した経験がありませんでしたが、今回弊社の「 やってみる 」というポリシーに則り、合宿の意義を改めて考察しながら実際に合宿を実施してその効果を検証しました。 「合宿を開催しました!」という記事は様々な企業が公開しておりましたが、合宿の目的や期待効果、振り返り等に言及している記事はあまり見当たりませんでしたので、その点をできるだけ整理してみようと考え今回このようなブログを作成させていただきました。 企業合宿を検討されている方の参考になれば幸いです。 動機 Insight Edgeでは、比較的規模の小さな様々なプロジェクトが同時に進行しており、メンバ一人一人が複数のプロジェクトを担当しています。 これらのプロジェクトは住友商事やグループ会社を中心とした各種顧客の要望に応じてPoC開発等の業務を行っています。 一方で、近年これらの顧客要望を基点にせず、独自のプロダクトを開発して事業開発を狙うプロジェクト、 Voiceek が登場しました。 プロダクト開発というスキームはInsight Edgeとしても新たなチャレンジになっており、持続的に機能を開発・提供していくために社内で経験のない新たな取り組みが様々必要になってきていました。 ひとまず体制は整備したものの、プロダクトマネージャやエンジニア、デザイナといったチームメンバのプロダクトに対する理解をそろえていくために議論は不十分でした。 また、DX技術専門会社としてデザイナを組織内に抱えているInsight Edgeは、デザイン起点で技術・機能開発を行うことが1つの強みです。 その価値を最大化するためには非デザイナへのデザイン起点でのアプローチの効果の理解をより深める必要がありました。 そのような議論点が多く存在しているものの、前述したように個々人が様々なプロジェクトを抱えている状況では全メンバが議論するためのまとまった時間を確保することがなかなかできませんでした。 そこで、Voiceekの関連するメンバでまとまった時間を確保して議論をするための手段として合宿を検討することにしました。 合宿の期待効果 上記の課題を解決するために、合宿という形態は適切な解なのでしょうか? 一般にはスタートアップ等を中心に様々な企業で合宿が行われています。 しかし、特にオフィスから離れたり宿泊を伴うような合宿は相応の費用がかかります。 その有用性は社員の特性にも依存するでしょうから、「やってみる」にしてもInsight Edgeとして今後も合宿を行うべきか?に対する回答を出せるような準備をしておく必要があるでしょう。 改めて企業における合宿の意義を考えてみましょう。 ChatGPTによれば、企業における合宿の意義は下記に整理できます。 チームビルディング・関係性強化 部門・職種を超えた信頼関係構築、コミュニケーションの質向上 戦略・ビジョンの再確認と共有 チーム・部門の方向性のすり合わせ、組織文化の醸成 創造的思考・集中討議の場 日常業務から離れた集中思考の時間 意思決定・合意形成のスピードアップ 関係者の共通理解が生まれやすい リフレッシュ・エンゲージメント向上 社員のモチベーション向上、普段見えない一面に触れる さすがChatGPT。それらしい回答が出てきました。 チームメンバの関係性はすでに一定築けているので、2. や3. の効果を期待して進めれば、上記の課題解決につながるアプローチになりそうです。 1. や5. もその価値は理解できるものの、チームメンバはすでに一定の関係性が築けていること、5. はそもそも付加的な価値と考えられることからここを主たる目的にすることは適切ではありません。 これらを考慮し、3. の意義を最大化する目的で、今回は普段の勤務地から離れた場所で一泊二日の宿泊を伴うものとしました。 ※合宿でのワークショップのイメージ 目標設定 今回の合宿には、Voiceekに関連するプロダクトマネージャ、エンジニア、デザイナ(UI/UXデザイナとデザインストラテジスト)総勢11名が参加しました。 合宿開始時点で、ロールごとに合宿終了時点でのありたき姿を下記の通り規定しました。 プロダクトマネージャ 顧客の理解を掘り下げる理由や顧客業務の掘り下げ方の理解を一段深める よりよりプロダクトを作るために、エンジニアに渡す前に整理すべき情報とエンジニアに共有すべき情報を理解する エンジニア デザイン起点の、人間中心アプローチがエンジニアリングにどのような意味があるのかを理解する 自身の業務の中でデザイナーをどのように活用するかの具体的なイメージを確立する デザイナ 他のロールがデザイナに期待する役割を明確化する Insight Edgeのプロジェクトにおけるデザイナ関与のデファクトスタンダードを構築する これを踏まえ、合宿全体の成果目標を下記の通り規定しました。 1. プロジェクトメンバのVoiceekに対する共通認識の構築 2. デザイン起点アプローチの価値認識 3. Insight Edgeにおける合宿の価値検証 Insight Edgeとして合宿が初の取り組みでもあったので、そもそも我々に宿泊合宿というスタイルが適合するのか?という点も検証を試みています。 これらが達成されたかどうかを、合宿の日々の振り返りアンケートや事後アンケートで確認し評価することにしました。 合宿の実施内容 Insight Edgeのデザインストラテジスト中心に合宿の具体的な進め方を設計しました。 デザインストラテジストは普段、顧客の将来像可視化・言語化、課題の発掘・探索をしています。 その中でワークショップの手法を用いて、ステークホルダとの対話の場を作ることもままあります( 弊社ブログのこちら も見てみてください)。 今回も、上記の目標設定を前提に合宿で実施するワークショップを設計しました。 項目 実施内容 予定時刻 Day1 オープニング 合宿説明、チェックイン、アイスブレイク 10:00-10:40 ワーク ペルソナドラマ(途中お昼60分) 10:40-13:10 ジャーニーマップ 13:10-15:55 クロージング ワークの発表会、チェックアウト 15:55-17:00 そのあと 懇親会等 17:00- Day2 オープニング 合宿説明、チェックイン、アイスブレイク 10:00-10:35 ワーク インサイト探索(途中お昼60分) 10:35-12:50 アイデア発想 12:50-15:30 クロージング ワークの発表会、チェックアウト 15:30-17:00 そのあと 帰路 17:00- 実施内容の詳細は社秘も含まれるため割愛しますが、いくつかポイントをまとめます。 実施時間 合宿期間中のワークショップの時間は一日あたり7時間にしています。 せっかく集中討議できる環境を整えているにも関わらず短いと感じるかもしれません。初めての合宿ということもあり、会社が定めている勤務定時時間内に収まる前提にした方が良いと考えたのもありますが、一番は”集中力”を気にしていました。人間の集中力には限界があり、時間を伸ばしても集中力が持たなくなりパフォーマンスが下がる可能性が高いため、デザインストラテジストの判断で7時間としました。 この長さについては事後のアンケートで参加者からフィードバックを得ています。 エンジニアも含めてユーザ目線のロールプレイを徹底 プロダクト開発においては顧客の深掘りは本来、多くの時間をかけて現場リサーチやユーザーインタビュー実施します。 ただし、今回は具体な機能開発のために実施するのではなく、人間中心のデザインアプローチがどのようにエンジニアリングに影響するかを経験する、またチームメンバがプロダクトに対して同じ目線を持つことを目的とした合宿のため、異なるアプローチを取りました。 具体的には、顧客業務理解の精度ではなく顧客目線を持つことの意義を腹落ちさせるために、「自分が顧客だったら」という設定で寸劇したり、想定ユーザを具体化してその視点で議論したりなど、ロールプレイを重視したアプローチにしました。 これはInsight Edgeのメンバはほとんどが中途採用のため、前職での経験からVoiceekの顧客業務をある程度想像できたことがポイントでした。その知識を議論にフィードバックすることで、空理空論には陥らずに議論を行うよう務めました。 これらを通じて、「こういう顧客の立場・環境から考えるとこういう価値が必要」という気づきから開発のイメージを作るまでを実施することで、エンジニアが開発を進める際に顧客像や顧客価値を理解しておくことの必要性等を体験・検証しました。 検証 合宿の効果検証は、合宿中のクロージングでのアンケート、及び日が空いての事後アンケートによって行いました。 設問 クロージングでのアンケート項目は以下の通りです。 Voiceekとはどのようなソリューションですか?一言で 合宿のワークショップで得られたことはなんですか? ワークショップの良かった点、改善点をあげてください 事後アンケートのアンケート項目は以下の通りです。 ロールごとに設定した合宿終了時点のありたき姿に対する達成度の自己評価(5段階評価+自由コメント) ワークショップの中身ではなく開催時間、進め方に対する評価(5段階評価) 合宿という形式に対する評価(各4段階評価) f-1. 合宿の開催時間内は普段の業務から離れ議論内容に集中できた f-2. 合宿をすることで普段以上に集中した議論ができた f-3. 合宿をすることでチームビルディングが促進された f-4. Voiceekに限らず、社内の集中した議論が必要な場合は宿泊を伴う合宿を開催すべきだ 結果 a. Voiceekとはどのようなソリューションですか?一言で この設問は、個々人が誰かと相談することなく、Voiceekを自身の言葉で表現するためのものでした。 具体な詳細は社外秘のため割愛しますが、表現の違いはあれど、その記載内容はほとんど同じものであり、 ロールを超えてプロダクトが向かうべき方向性の共通認識が生まれました 。 b. 合宿のワークショップで得られたことはなんですか? 回答結果を生成AIを活用してサマライズした結果の一部は以下の通りです。 VoC分析の目的の明確化 ペルソナ設定とカスタマージャーニーの重要性認識 仮説検証プロセスの重要性 プロジェクトメンバのVoiceekへの共通認識が芽生えたことが改めて確認できました。 さらに、この結果からデザイナではないメンバにおいても デザイン起点アプローチの価値認識が深まった と評価できると思います。 c. ワークショップの良かった点、改善点をあげてください 良かった点 多角的な視点での議論と深い理解の促進 効果的なワークショップ設計と進行 チーム間の理解の促進と共通認識の形成 改善点 事前準備・情報共有の強化 参加者の理解度を揃えるために、前提情報を事前に共有するとよい 事前課題(宿題)を用意し、議論の密度を高める 進行の調整と時間配分の最適化 ワークショップの時間が足りずアイデアの広がりが制限されたため、時間配分を見直す ワークショップの設計と議論の進め方の工夫 ワークショップを設計するにあたって、メンバも普段の業務で忙しいので事前課題や準備を行うのは無しにしていたのですが、そのような状況にあっても事前準備に前向きなフィードバックが得られたのは驚きでした。 d. ロールごとに設定した合宿終了時点のありたき姿に対する達成度の自己評価 5段階評価で平均3.9はまずまずと行ったところでしょうか。 コメントとしては、 議論が仮説ベースであり、より具体的な顧客情報が必要 人間中心アプローチの業務適用の理解が進んだが、デザイナとの協働や役割分担の明確化には至らず と言ったものがありました。 e. ワークショップの中身ではなく開催時間、進め方に対する評価 平均4.3は非常に良かったのではないでしょうか。 特に開催時間については、前述の通り短いのではないか?という感覚もあったのですが、参加者の評価も適正であり、改めて人間の集中力の限界を知るとともに今後合宿やワークショップを設計する際も詰め込みすぎには注意すべきという重大な示唆が得られました。 f. 合宿という形式に対する評価 f-1、f-2は非常に良い評価でした。 今回のワークショップでは、個々人が様々なプロジェクトを抱える中でなんとか日程調整をしてもらったのですが、参加する以上は集中しようという意欲が非常に強く、ワークショップ中は社用携帯やPCをほとんど見ず議論に集中できていました。 f-3の「あまりそう思わない」については、すでにチームの関係性が構築されているため、という補足がありました。 f-4の評価は手放しで賛同、という人は半数程度であり、宿泊合宿はやはりその目的を鑑みて慎重に実施する必要がありそうです。 総括として、Insight Edgeとして合宿をする意義として、より深い議論するためには有用であると結論付けられました。 今後合宿を企画する際は、この結果を振り返りながら合宿の設計を行うことでより価値の高い合宿になることでしょう。 結び 合宿というのはさまざまな付加価値があると思うのですが、特に社員全員参加でない場合はどうしても「遊んでいる」という印象が強くなりがちです。 なかなか定量的な評価を行うことは難しいものの、今回定性的な効果は一定認められたのではないかと考えております。 企業文化、社員や参加メンバの特性によっても合宿の意義・効果は異なるでしょうが、少しでも読んでいただいた方の参考になれば幸いです。
アバター
はじめまして!Data Scientistの市川です。 今回は、先日第34回 人工知能学会 金融情報学研究会(SIG-FIN) に行ってきましたので、そのレポートをさせて頂ければと思います。 イベントの概要 発表の概要 SIG-FIN UFO-2024 タスク(6件) (01) 有価証券報告書の表を対象としたUFO-2024コンペティション (02) 表構造の理解と表項目の説明文生成に基づくTable QAタスクへの挑戦 (03) 有価証券報告書の表質問応答を対象としたSIG-FIN UFO-2024タスクにおけるUTUtLB25チームの性能評価 (04) 二値分類モデルに基づく有価証券報告書を対象とした表解析手法の提案 (05) 有価証券報告書の表理解タスクに対する解法の提案 (06) 類似した質問と表に基づく表検索及び大規模言語モデルを用いたOne-shot表質問応答 人工市場・投資戦略(5件) (08) 人工市場を用いた規制を考慮したETF・原資産市場間のショック伝搬の分析 (09) LLMベースのエージェントによる人工市場シミュレーションの構築 (10) 重み付きTsallisエントロピー正則化に基づくリスクパリティ・ポートフォリオの一般化 (11) 推薦システムにおける多様性を利用したアセットアロケーション 金融市場(6件) (12) 人工知能学会 金融情報学研究会(SIG-FIN)の歴史 - AIと金融の技術史の一部として議論 - (13) 経済状況に応じてベーシックインカムがもたらす変化 (14) 経済フェルミ推定問題: 因果構造推論による推定の改善 (15) 東京証券取引所の市場再編における売買高への影響について (16) 資産価格のジャンプとグラフによる資産の関係性の異常検知手法の提案 (17) アート資産の分散投資効果の実証分析 テキストマイニング・LLMs(5件) (18) 金融テキストを対象とした強弱を捉えることができるセンチメントモデル開発のためのデータセット構築方法の検討および分析 (19) 景気ウォッチャー調査のデータセット構築と物価センチメント分析 (20) 決算短信を用いた業種ごとの年間レポートの自動生成 (21) LLMを用いた情報系企業のESG評価についての一考察 (22) LLMによる大量保有報告書における担保契約等重要な契約に関する情報の構造化 機械学習1(5件) (23) 多様なテンプレートと合成データを用いた大規模言語モデルの業種区分予測における知識抽出 (24) 機械学習による企業業種分類の定量評価と応用 (25) 機械学習を用いたクロスセクション予測における株価および業績シグナルの比較分析 (26) Multi-step Utility Lossを用いた深層学習モデルよる動的ポートフォリオ最適化 (27) ボラティリティ推定機能を持つ条件付き拡散モデルによる金融時系列生成 機械学習2(5件) (28) ベイズ推定における事前分布の作成にChatGPTを用いた暴落時の日経平均株価の予測 (29) 深層生成モデルによる金融市場の注文板の生成 (30) 機械学習モデルを用いたシミュレーション分析:地方債市場における会計情報の寄与 (31) KANベース自己符号化器を用いた国債イールドカーブのファクターモデルの構築 (32) 有価証券報告書と統合報告書を活用したESG投資のためのオントロジー構築と生成AIによる情報抽出 雑感 イベントの概要 人工知能学会 金融情報学研究会(SIG-FIN) は人工知能学会の第二種研究会です。 詳細は上記リンクに譲るのですが、近年より広い方々の金融市場への関心が高まっています。このような背景で、ファイナンス分野への人工知能技術の応用を促進するための研究会になります。人工知能分野の研究者や金融市場の現場の技術者が参加する、大変ユニークな研究会になっています。 最近、かなり発表量が増加傾向にあり、聴いているだけでも忙しい研究会です。例年、土曜日の1日のみの開催でしたが、発表数の増加に伴い、土日を両方使う研究会となりました。 概要は以下の通りです。 * 日時:2025年3月1日(土) および 3月2日(日) * 開催形式:会場およびオンライン(Zoom使用)のハイブリッド開催 * 会場:KPMGコンサルティング 第34回研究会 発表の概要 こちらの研究会はありがたいことに 各発表の概要pdfが公開されています 。 以下、著者の敬称略とさせて頂きます。 SIG-FIN UFO-2024 タスク(6件) (01) 有価証券報告書の表を対象としたUFO-2024コンペティション 木村 泰知, 佐藤 栄作 (小樽商科大学), 門脇 一真 (日本総合研究所), 乙武 北斗 (福岡大学) UFO-2024のタスクの概要 https://sites.google.com/view/ufo-2024/ データセット https://github.com/nlp-for-japanese-securities-reports/ufo-2024/ 有価証券報告書の37%が通常の機械判読では難しい複雑な表と言われている。 UFO-2024 では、TOPIX500の有報を対象とした表検索(Table Retrieval)タスクと表質問応答(Table QA)タスクがある。 このセッションでは、コンペの上位者の解法をシェアする場になっている。 (02) 表構造の理解と表項目の説明文生成に基づくTable QAタスクへの挑戦 髙砂 爽, 秋葉 友良 (豊橋技術科学大学) アプローチの大きな特徴は、表を「見出し部分(ヘッダ)」と「データ部分」に分割し、セルをテキスト化することで自然言語処理技術(BERTやBM25など)を適用可能にしている点です。 表の前処理と正規化 - HTML形式の有価証券報告書から表を抽出し、セルの文字列を正規化(全角・半角や日付の統一、通貨・数値の単位変換など)します。 - 行全体・列全体・表全体にかかる単位を検出して数値をスケーリングし、セル単体の単位も検出して処理する仕組みを導入しています。 TDE(Table Discriminative Explanation)を用いた表構造解析 - 各セルを「Metadata」「Header」「Attribute」「Data」の4種に分類するタスク(NTCIR-17 UFOのTDEサブタスク)をBERT分類器で実装し、その結果をもとにテーブルを「上側(見出し)」「左側(見出し)」「右下(データセル)」の領域に分割します。 - 分割の際は、見出し部分をHeader/Attribute/Metadataセル、データ部分をDataセルとしてスコア(TSS)を最大化する分割位置を探索します。これにより、多くの場合は上数行×左数列が見出し、残りがデータとして認識されます。 表のテキスト化(文章生成) - 分割後のテーブルから、見出しセルとデータセルの組合せをテンプレート「XのYはZです。」で文章に変換します(Xは上側見出しの結合、Yは左側見出しの結合、Zはデータセルの値)。 - たとえば「第44期」「売上高」「458140」の3セルが対応していれば「第44期の売上高は458140です。」のように文章化。これにより、テーブルの中身を自然言語の文書として扱えるようになります。 TableRetrievalタスク - 論文IDごとに抽出した各表をテキスト化した文書にまとめ、BM25によるベクトル検索を用いて「質問文に最も合致する表」を探す、という構造になっています。 - 実験ではNTCIR-18 UFOデータやSIG-FIN UFO2024データを用いて検証が行われ、工夫としては「表上部にある説明文を追加する」「空セルの扱いを工夫する」などの改善により、精度を向上させています。 TableQAタスク - 与えられた表ID(TableRetrievalによって特定されたもの、またはタスクで指定されたもの)と質問文をもとに、「どのセルが答えか」を見つけて値を抽出します。 - 具体的には、質問文と各セル(テキスト化した文章)をBERT分類器で2値分類(適合/不適合)し、適合と判断されたセルを回答候補とします。 - 学習にはNTCIRやSIG-FIN UFO2024で提供されたクエリ(質問)と正解セルのデータを使い、さらに空文字セルなどは除外するといった最適化を行うことで、座標および値の正解率を向上させています。 実験結果と考察 - TableRetrievalタスクでは正解率0.341程度、TableQAタスクではセル座標の正解率0.743、値の正解率0.701といった成果を得ています。従来のベースラインやGPT-4モデルを用いた手法と比べても、TableQAの値抽出精度が上回るなどの有望な結果が示されています。 - 一方で、表が複雑に結合されているケースや、セル内に長文が記載されているケースなどは文章化が難しく、さらなる精度向上には「より柔軟な表分割手法」「Text-to-Text生成の活用」などが必要であるとしています。 (03) 有価証券報告書の表質問応答を対象としたSIG-FIN UFO-2024タスクにおけるUTUtLB25チームの性能評価 司 龍, 張 引, 王 小天. 宇津呂 武仁 (筑波大学) SIG-FIN UFO-2024タスクのうち「表質問応答(Table QA)」サブタスクを対象としています。 LLMを用いて課題解決を試みています。 データセット 対象データ EDINETから取得したTOPIX100企業の有報(HTML形式)をもとに、HTML→CSVへ変換した表データを使用し、自然言語の質問文を生成して構築。最終的に合計496文書・8,116表・13,675件の質問文(訓練・検証・テスト)に分割している。 質問文の生成 CSV形式の表に対するテンプレートを作り、ChatGPTを用いて様々な言い回しの自然言語に変換することで、多様性のある質問文を作成。 表質問応答手法 タスク設定 - 入力: 質問文、および該当する表ID(Table ID) - 出力: 質問への解答としてセルの座標(Cell ID)またはセルの値 - 評価: 解答が正答と一致するかどうかのAccuracy(正解率) LLMを用いた表QA - プロンプト設計 - システムプロンプト・ユーザープロンプトにおいて、回答フォーマットや出力を厳密に指定。 - Temperatureを0にするなど、確率的な変動を抑えて安定した出力を得る。 - Zero/Few-shot学習 - サンプルとして提示する質問・回答例(ショット数)を変化させ、精度がどう向上するかを調査。 実験結果 - GPT-4・Claude 3.5・Gemini・Grokなど複数LLMを用い、検証用データに対する「Cell ID」と「Value」それぞれの正解率を測定。 - 最も高精度 だったのはAnthropic社の「Claude3.5-Sonnet」で、セル座標の正解率90%、値の正解率93%を達成。 - 多くのモデルでFew-shot学習によって精度が向上するものの、ショット数を増やしすぎると逆に精度が落ちる場合もあり、適切なショット数の選択が重要となる。 - 誤り分析では、ハイフンを「0」と解釈する誤りや単位のミスなどがショット数増加で減る一方、セルを取り違える誤りは残存した。 NTCIR U4タスクとの比較 - SIG-FIN UFO-2024と同様に有報の表QAを扱うNTCIR-18 U4タスクでも同手法を適用しており、ClaudeやGrokなど複数モデルで高精度を実現。詳細は別の論文で議論しているが、U4とUFO-2024を通じて、LLMが大規模な日本語表QAにおいて高い性能を発揮することを確認。 結論 LLMを用いた日本語有報の表質問応答で高精度が得られることを示し、特にFew-shot学習は精度向上に大きく貢献する。一方でセルの取り違いなど特定種の誤りはまだ残っており、追加対策の必要性が示唆される。 (04) 二値分類モデルに基づく有価証券報告書を対象とした表解析手法の提案 藤田 優希, 水島 陵太, 乙武 北斗, 吉村 賢治 (福岡大学) それぞれに対して二値分類モデルを中心とした手法を提案しています。 Table Retrievalの場合 1つの有報内に多数存在する表の中から、与えられた質問(自然言語)に対して「答えが含まれる表」を特定するタスク。膨大な候補表へのアプローチとして、 二値分類モデル を最終ステップに用いるが、データの不均衡や計算量の問題を解決するために以下の多段階処理を導入。 ルールベースの前処理 - HTMLのtableタグ内のth/tdからテキストを抽出し、数字・一般的な語(「百万円」「合計」など)や記号の削除、日付表記の正規化などを行う。 質問文からセクションを予測して絞り込み - 有報は第一部・第二部など複数のセクションに分かれており、質問文から「どのセクションに属するか」を多クラス分類モデル(BERT)で予測し、該当セクションの表だけに候補を絞る。 Embeddingモデル(多言語E5)によるコサイン類似度でさらに候補削減 - 質問文と表の埋め込みを取得し、コサイン類似度が高い上位100件を残す。 Rerankモデル(ruri-reranker)を適用 - 類似度上位100件をさらにスコア順に並び替え、上位最大50件を候補とする。 二値分類モデル(BERT) - 候補となった最大50件を正例(答えを含む表) or 負例(含まない表)に分類。最終的にスコア最大の表を回答とする。 - 分類層の構造(全結合層数、質問と表の連結方法など)を比較し、一部構成を変更したモデルが最も高い精度(約0.84)を達成。 結果 - Publicスコア(Accuracy)で約0.84を得て、ベースライン(約0.21)を大きく上回る高精度を達成。 - 前処理が著者の主観に依拠しており、より客観的・体系的なルール構築が必要。 Table QA - 与えられた質問文に対して、該当する「セルの位置(Cell ID)」および「セル内の値(Cell Value)」を特定する。 - 有報の表はセル結合(rowspan, colspan)が多様で、単位や数値表記もまちまちであるため、フラットなテキスト化だけでは構造が十分に捉えられない。 表の構造解析(RCI) - HTML表を行・列ごとに分割し、行・列それぞれが質問に対してどの程度関連するかを二値分類モデルで判定。最も関連度が高い行と列の交点をセルの位置とみなす。 - モデルとしてはDeBERTa V3を採用。 値の正規化(T5) - 抽出したセルに含まれる数値・単位のズレを「T5」による生成タスクで正規化。 - たとえば「1,000千円」や「△302 百万円」などを共通の表現に変換する。 結果 - Cell IDの特定精度は約0.91、Cell Value(単位考慮)の正解率は約0.87を達成。 - GPT-4など大規模LLMベースラインの値予測精度(最大0.74前後)を上回る。 結論 - Table Retrieval : 一連の多段階フィルタリング+二値分類モデルで、高い正解率(約0.84)を実現。 - Table QA : 表の行・列それぞれへの二値分類モデル(RCI)でセル位置を特定し、T5で値を正規化する分割アプローチが、LLM単体を用いた方法より高精度を示す(Cell Value約0.87)。 (05) 有価証券報告書の表理解タスクに対する解法の提案 別木 智也 (MTEC) 特徴としては、有報内のヘッダタグ(h1~h6)を活用した「目次データベース」を学習データから構築し、質問文のキーワードやLLM(GPT-4o-mini)を段階的に組み合わせて候補表を絞り込む点にあります。 データセット - 対象はTOPIX500の企業が2023年7月~2024年6月に提出した有報(HTML形式)。 - 学習データ・評価データ・テストデータに分割され、計493社・約13,673件の質問文が用意されている。 - 各HTMLには複数の <table> 要素が含まれ、一意のtable-idが振られている。 提案手法の概要 著者の手法は「目次情報(h1~h6)を使った段階的な絞り込み」と「質問文のキーワード抽出」「LLMによる最終的な判定」という3ステップで構成されています。 目次データベースの作成 - 学習データ(企業と、その企業のHTML内のtableに対する正解表)を使い、各テーブル(table-id)を含むHTMLのh1~h6タグ情報(ヘッダー)と紐づけて「目次データベース」を構築。 - 例えば「S100RX3W-0101010-tab2」はh1:第一部企業情報、h2:企業の概況 …などの見出しを持つ、というようにJSON形式で保存。 目次を利用した絞り込み 1.キーワード抽出 - 質問文は「○○年度の●●決算における『▲▲、△△』は?」など画一的 - 鍵括弧内の語や「連結決算/個別決算」などをキーワードとして抽出 2.学習データから同じキーワードを含む質問を検索し、当該質問の正解table-idに対応する目次情報を取得 - 当該ヘッダー情報(h1~h6)と一致するテーブルだけを候補とする。 - 目次段階での絞り込みにより、不要なテーブルを大幅除外。 キーワードを利用した絞り込み - 目次段階で候補が複数残った場合、それら候補テーブルのヘッダー・セルテキスト中の「キーワード出現数」がもっとも多いtable-idを選ぶ。 - 出現数が同数の場合は複数残す。 LLMを利用した絞り込み - table-idが一意に絞れない場合に、GPT-4o-miniを用いて最終決定を行う。 - 質問文を自然な形へリライトし、各テーブルのテキストとともにLLMに投げ、最適なtable-idを構造化出力させる。 評価 - 検証データでの段階的絞り込みごとの再現率(Recall)を計測。 目次 :約99.7% キーワード :約96.3% LLM :約75.1%(=最終Accuracy) - LLMステップで大きく精度が落ちる理由として「経営指標等」など多義的表現が複数テーブルに該当したり、自然言語変換時に情報が省略・誤訳されるケースが挙げられる。 (06) 類似した質問と表に基づく表検索及び大規模言語モデルを用いたOne-shot表質問応答 田中 麻由梨 (JPX総研), 土井 惟成 (日本取引所グループ) 類似した質問と表を活用した検索 質問文の埋め込みと類似質問の取得 - 事前学習済みモデル(RoBERTa)を利用し、学習データ(Trainセット)の質問文をベクトル化。 - テストデータ(Testセット)の質問文を同モデルで埋め込み、Trainセットとコサイン類似度を計算して最も近い質問を選ぶ。 BLEUスコアを用いた表類似度計算 - 得られた「最も類似するTrainの質問」に紐づいている表(1つ)をテキスト化(角括弧の二重リスト形式など)し、Testセットに含まれる各表のテキスト化結果とのBLEUスコアを計算。 - 最もBLEUスコアが高い表を最終的な解答とする。 このプロセスにより、まず似た質問文を介して関連表を当たりをつけ、その上でテキスト化した表同士の類似度をBLEUで評価する多段階検索を実現している。 表質問応答タスクの提案手法 LLMを用いたOne-shotプロンプティング - Testセットの各質問に対し、同じく「最も類似するTrainの質問」を検索し、その質問+正解セルを含む表のペアを「One-shotのサンプル」としてLLM(ChatGPT-4o)に与える。 - プロンプト中では「サンプル質問&表 → 正解セルID」を示し、同じフォーマットでTest質問に対応するセルIDを出力させるよう誘導。 - まずセルIDを抽出し、その後に値を取り出す手順を取ることで、LLMが正確なセルを指定しやすくする狙いがある。 実験 - データ : SIG-FIN UFO-2024のTrain・Testセットを使用。 - 表検索 : Accuracy=0.7320。役員個人名などTrainに登場しない固有情報への対応が弱点。 - 表質問応答 : - Zero-shot(サンプルなし): セルID正解率=0.6727、値の正解率=0.1462。 - One-shot(サンプル提供): セルID正解率=0.7326、値の正解率=0.1776。 - One-shotによりセルIDの精度が向上しており、適切なサンプルを提示する効果が確認された。一方、値の正解率が低いのは単位や数値の表記揺れに対処していないためと考えられる。 人工市場・投資戦略(5件) (08) 人工市場を用いた規制を考慮したETF・原資産市場間のショック伝搬の分析 遠藤 修斗 (工学院大学), 水田 孝信 (スパークス・アセット・マネジメント), 八木 勲 (工学院大学) ETF(上場投資信託)とその原資産市場(2つの原資産市場)を人工市場シミュレーションで構築し、そこに誤発注やファンダメンタル価格の下落といった「ショック」が発生した際に、裁定取引を通じて市場間でどのように価格下落が伝搬するかを分析しています。さらに値幅制限を代表とする規制の有無がショック伝搬の程度にどのような影響を与えるかを調査する点が特徴です。 目的と背景 - ETFと、それを構成する複数の原資産市場との間では裁定取引が行われるため、一方の市場でショックが起こると他方へも価格変動が波及する可能性が高い。 - リーマン・ショックやフラッシュ・クラッシュのような急激な下落を防止するために、証券取引所などでは値幅制限・サーキットブレイカーといった規制が導入される。しかし市場によって規制導入の状況は異なる。 - 過去の実証データだけでは各規制の直接的な効果を明確に切り分けるのが難しいため、マルチエージェント・シミュレーション(人工市場)を用いて規制の影響を定性的に分析する。 主な結果 - ショック伝搬の確認 : いずれの場合も、裁定取引によってショックを受けた市場の価格下落が他市場へ伝わることを確認。 - 値幅制限の影響 - 原資産市場側にショックが起こった場合は、原資産市場への値幅制限導入で自市場やETF市場の下落幅が小さくなるケースが多い。 - 一方でETF市場への誤発注ショックの際は、原資産市場に値幅制限があると価格差が拡大してしまい、かえってETF市場側の下落が抑えられにくい場面が見られた。 - 原資産市場1のファンダメンタル価格下落時は、当該市場に値幅制限があれば下落が緩和される一方、ショックを受けていないもう一方の原資産市場が値幅制限を導入していると、その市場はかえって他市場から伝わる下落が大きくなる場合があった。 - 総括 : 「どの市場にいつ値幅制限を導入するか」でショックの拡大・抑制効果が変わることが示唆された。特に、ある市場の下落が裁定取引を介して他市場の価格をも押し下げる過程で、規制の有無がダイナミクスを複雑化させる。 (09) LLMベースのエージェントによる人工市場シミュレーションの構築 平野 正徳 (PFN) 大規模言語モデル(LLM)を利用したエージェントを人工市場シミュレーションに導入し、伝統的な“ノイズトレーダーモデル”では再現しにくい金融市場特有の性質(いわゆるスタイライズド・ファクト)をどこまで再現可能かを検証した研究です。 背景と目的 - ChatGPTやGPT-4といったLLMの登場により、人間のようにテキストを入出力できる「思考を持ったかのような」振る舞いが一部可能になっている。これをエージェントとみなし、各種シミュレーションへの応用が期待されている。 - 既存の人工市場シミュレーションはエージェントの振る舞いを数式化する場合が多いが、それだと実際の投資家の複雑な意思決定を忠実に表現するのは難しい。そこで、言語モデルを用いたエージェントを導入することで、より人間らしく複雑な意思決定をする株式トレーダーを実装できないかを探る。 人工市場モデルの構築 エージェントの種類 LLMエージェント 大規模言語モデルにプロンプト(市場情報・過去価格・ファンダメンタル等)を与え、指値または成行の売買判断を行う。 LLMへのプロンプト内容(ファンダメンタル・ポジション情報・プロスペクト理論の考慮など)を複数パターン用意して効果を比較。 ノイズエージェント 買い/売り価格をランダムに決める。外部要因による取引をモデル化するためのエージェント。 実験設計 実験パラメータ LLMエージェントの割合:0%, 10%, 20% LLMエージェントのプロンプト構成:ファンダメンタル、ポジション、プロスペクト理論の3要素をオン/オフ → 全8通り それぞれの条件下で100回のシミュレーションを行う。 評価指標 : 金融市場の典型的特徴(スタイライズド・ファクト)の一部に着目 リターンの平均がほぼ0(もしくは極めて小さい正) 尖度(ファットテール度合い)が大きい(>3) 歪度が負(下落側に厚みをもつ分布) 実験結果 - ノイズエージェントのみではファットテール性をはじめ多くの要素が再現困難。先行研究と同様、ランダム注文だけでは価格分布が正規分布に近くなり、尖度が小さい。 - LLM導入による改善とプロンプトの影響 - LLMエージェントを追加すると、尖度が増大し歪度も負になる傾向が出てくる。 - ただし「どのようなプロンプトを与えるか」が非常に重要で、些細な変更で結果が大きく変わる。 - 実験では以下のような一般傾向が見られた: 1. ポジション情報をプロンプトに含める : 歪度が負になるケースが多い(LLMが自分の持ち高を理解し、売りポジションを取りやすくなる)。 2. ファンダメンタル価格を含める : 価格が大きく動いた際にファンダメンタルへ回帰する意識が働くようで、リターンの分布裾が重くなり、尖度が大きくなる。 3. プロスペクト理論の記載を含める : リスク回避度合いが変わり、下落局面でボラティリティを増す動きが生じる場合がある。 - 結果として、 ファンダメンタル・ポジション・プロスペクトの3要素をすべて含むプロンプト を与えたLLMエージェントが存在する場合に最もスタイライズド・ファクトに近い分布になった。 (10) 重み付きTsallisエントロピー正則化に基づくリスクパリティ・ポートフォリオの一般化 中川 慧 (野村アセット/大阪公立大学), 土屋 平 (東京大学) 従来の平均分散法やリスクベースのポートフォリオ構築における課題を克服するため、 重み付きTsallisエントロピー を正則化項として組み込んだ新たなポートフォリオ最適化手法を提案しています。特に、通常のリスクパリティやリスクバジェッティング・ポートフォリオは期待リターンを考慮しないため、リターン面で非効率になりやすいという問題に対し、Tsallisエントロピーを用いて銘柄分散と期待リターンの両方をバランスよく取り入れる点が本研究の特徴です。 (11) 推薦システムにおける多様性を利用したアセットアロケーション 星野 知也 (三井住友銀行) 推薦システムにおける多様性(diversity)の概念を資産配分(アセットアロケーション)に適用し、集中投資によるリターン追求と分散投資によるリスク低減を両立させる新たなポートフォリオ構築手法を提案しています。推薦システムの分野では、利用者の選好に合うアイテムを提示する一方、類似アイテムばかりを冗長に提示すると満足度を下げてしまうため、関連性(relevance)と多様性(diversity)のトレードオフが重要視されます。これを「投資家の見通し(リターン追求)と資産間の類似度(分散投資)」の問題に対応づけて、以下の2つの代表的手法を活用しています。 手法の概要 行列式点過程(Determinantal Point Process, DPP) - フェルミ粒子系のように「要素間に斥力(repulsion)が働く」点過程モデル。 - 離散集合から部分集合を選択する際、要素間の類似度が高い組合せが同時に選ばれにくくなる性質をもつ。 - 各資産への「見通し」スコア(類似度の対角成分に対応)と、資産間の相関などの類似度(非対角成分)を行列 ( L ) に組み込み、行列式を最大化することで関連性と多様性を両立できる。 周辺関連性最大化(Maximal Marginal Relevance, MMR) - 情報検索や推薦システムでよく使われる再ランキング手法。 - 「利用者クエリとの関連度」と「既に選択したアイテムとの非類似性」を線形結合してスコアを定義し、アイテムを段階的に選んでいく。 - アセットアロケーションにおいては、投資家の見通し(関連性)と資産間の類似度(非類似性を大きくして多様化)をバランスよく組合せる。 実証分析のポイント 対象銘柄 : S&P 500構成銘柄(2015~2024年の10年データ) 比較対象 : 最小分散ポートフォリオ (MV) 逆ボラティリティ (IV) 等ウェイト (EW) モメンタム上位銘柄 (Mom) ブラック-リッターマン・モデル (BL) 手法 : DPP: 資産間の相関行列やグラフラプラシアンを用いて多様度を制御。 MMR: 資産を1つずつ貪欲に追加し、多様性を確保。 まとめと意義 - 新しい視点の導入 : 推薦システムで検討されている「関連性 vs. 多様性」の枠組みをアセットアロケーションに応用し、銘柄選択でリターン追求とリスク分散を同時に考慮する。 - 理論的背景 : DPP・MMRは元々情報検索・機械学習の分野で研究が進んでおり、多様性に特化した優れた性質を持つ。これを資産間の相関構造や投資家の見通しに組み合わせることで、新たなポートフォリオ最適化を提案。 - 実証的評価 : S&P 500銘柄を対象とした検証では、従来のリスクベースやモメンタム戦略に対し、リスク調整後のパフォーマンス向上やドローダウンの抑制といった優位性が見られた。 金融市場(6件) (12) 人工知能学会 金融情報学研究会(SIG-FIN)の歴史 - AIと金融の技術史の一部として議論 - 水田 孝信 (スパークス・アセット・マネジメント) 人工知能学会 金融情報学研究会 (SIG-FIN) の創設から約16年余りの歴史を振り返りながら、金融とAI(人工知能)の歩みを概観したものです。2008年の研究会発足当初は参加者が少ない時期もありましたが、第3次AIブームに連動して発表件数や参加者数が急増し、さらにコロナ禍でオンライン化を経験して現在まで継続してきた経緯を整理しています。金融情報学研究会が日本におけるAIと金融の発展にどのように寄与してきたかが、時代ごとの出来事とともに記されています。 (13) 経済状況に応じてベーシックインカムがもたらす変化 高島 幸成 (白鴎大学), 八木 勲 (工学院大学) 研究の背景と目的 ベーシックインカム(BI)は、全ての人に無条件で一定の所得を給付する制度であり、貧困対策や社会保障の簡素化などの観点から近年注目を集めています。一方で、BIを全面的に導入した際の労働意欲(労働供給)が低下する懸念や、社会・経済全体に及ぼす影響が不確実とされ、従来は実証実験や限定的な理論研究に留まっていました。 本稿では、このような不確実性を解消する一手段として、相対的な所得に対する効用(他者との所得格差を意識した労働意欲の変化)を取り入れた複層的なフィードバック構造を持つエージェントベース・マクロ経済モデルを構築し、景気の好調期や低迷期など“経済状況の違い”によってBIの効果がどのように変化するかを分析しています。 (14) 経済フェルミ推定問題: 因果構造推論による推定の改善 安田 卓矢, 村山 友理, 和泉 潔 (東京大学) 経済フェルミ問題(Economic Fermi Problems: EFPs)において、複数のLLMにより回答と評価を一貫して実行する仕組みを提案し、さらに 因果構造推論(Causal Structural Reasoning) を組み込むことで推定精度を高めることを目指しています。 (15) 東京証券取引所の市場再編における売買高への影響について 丸山 博之 (拓殖大学) 東京証券取引所では、2022年に東証一部・二部・マザーズ・JASDAQ(Standard/Growth)の4区分を、プライム市場・スタンダード市場・グロース市場の3区分へ再編しました。本研究では、この市場区分の変更が個別銘柄(本稿では水産・農業関連銘柄)における 売買高 の変化に影響を与えた時期を特定することを目的とし、 閾値自己回帰モデル(Threshold Autoregression) を用いて分析を行っています。 データ - 日次ベースの売買高などをJPXデータクラウドから取得。 - 市場再編の実施日を基準に前後3か月(計6か月)のデータを対象。 - 水産・農業セクターの銘柄をサンプルとして使用。 閾値自己回帰モデル : - 時系列データ x_t に対し、ある閾値 c を境にパラメータが異なる2つの回帰式を適用する。 - 本研究では y_t = t / T (t: 時点、T: 全期間) を閾値判定に用い、売買高の対数平均値についてモデルを推定。 主な結果 3つのグループいずれにおいても実際の市場区分が変更される“前”に売買高に明確な変化が確認されました。具体的な閾値(状態が変化した日)は以下の通りです。 東証一部→プライム市場 : 2022年2月2日に閾値を境に状態変化 東証一部→スタンダード市場 : 2022年2月16日に状態変化 JASDAQスタンダード→スタンダード市場 : 2022年3月11日に状態変化 実際の市場再編が行われたのは4月4日ですが、 早い銘柄では2月初旬から売買高の変化が起きている と推定されました。 市場再編の公式実施日より前に、投資家は区分変更を見越した売買・資金移動を行った可能性が高い。 東証一部からプライム市場へ移行した銘柄が最も早く状態変化が生じ、JASDAQスタンダード→スタンダード市場の銘柄はやや遅れて変化が起きている。 これは市場区分ごとに投資家からの注目度や事前の思惑の度合いが異なることを示唆する。 (16) 資産価格のジャンプとグラフによる資産の関係性の異常検知手法の提案 中田 喜之, 吉野 貴晶, 杉江 利章 (ニッセイアセットマネジメント), 関口 海良, 劉 乃嘉, 大澤 幸生 (東京大学) 背景と目的 資産運用においては、複数資産のリターンの関係(相関)をもとに配分(ポートフォリオ構築)を行うリスクベース戦略がよく用いられます。しかし、市場環境によって資産間の関係は大きく変化するため、そうした変化をいち早く検知できないと運用パフォーマンスの低下やリスク増大を招きます。本研究は、 資産価格のジャンプ(不連続な価格変動)が同時に発生する資産の集合をグラフとして表現 し、そのグラフ構造と直近の関係性の差異から、資産間の関係を変化させるようなイベント(サプライズ)を早期に検知する手法を提案しています。 ジャンプ 資産価格の ジャンプ (突発的な急騰・急落)は、市場に大きな影響を与えるイベントやサプライズなニュースで生じる可能性が高いと言われています。そこで以下の手順でジャンプを検知します。 Lee and Mykland (2008) の手法を用いて、5分足データから価格変動率が異常に大きい箇所を非パラメトリックに判定。 有意水準を非常に厳しく設定(例: 0.01%)することで、明確なイベントに起因する大きなジャンプのみを抽出。 検出されたジャンプには正(上昇)・負(下落)の方向が付与される。 グラフ分析と領域間相互作用 - 直近120日間の日次リターンに基づき主成分分析を行い、主成分の符号(プラス/マイナス)が同じ資産同士を一つの“領域”として区分。 - ある5分間隔で複数の資産が同時にジャンプし、なおかつジャンプ方向が同じ資産同士をエッジで結んだグラフを構築。 - グラフが上記“領域”をまたいで接続が生じているほど、直近の相関構造(資産関係)とは異なる反応と見做し、 領域間相互作用(Inter-domain linkage) という指標で定量化。 - この指標が高いほど「既存の関係性から逸脱した同時ジャンプ」が起きたと判断し、資産間関係に影響を与えるイベントが発生した可能性が高いとみなす。 検出したイベントの影響評価 - 直近120日の日次リターンで推定した共分散行列が、イベント検知後どれくらい変化するか(平均二乗誤差・対数尤度など)を観察。 - また、共分散行列を用いたリスクベース戦略(最小分散ポートフォリオ、リスクパリティなど)を組んだ場合、イベント後のパフォーマンスがどの程度悪化するかを統計的に検証。 データと設定 - 対象資産: 為替(ドル円・ユーロドル)、主要先物(米・独・日国債、米・欧・日株価指数、金、原油)計10種類。 - 5分足の気配値データ(約20年分)に対してLee & Mykland手法でジャンプを検出し、日次リターンを使って共分散行列を推定。 イベント検知 - ジャンプのうち、 領域間相互作用 が高いケース(既存の資産関係と異なる反応を示すジャンプ)が確認された。 - 実際にそうしたジャンプが検知された時刻を調べると、 米国金融当局(FRB、FOMC)や主要経済指標の発表 、または日銀やECBの政策発表など、マーケットに大きな影響を与えるイベントが多く対応。 - 逆に領域間相互作用が0に近いジャンプは、既存の相関構造に沿った動き(例: 通常のリスクオフで株と原油が下落し、債券が上昇など)に留まるケースが多い。 共分散行列とリスクベース戦略への影響 - 領域間相互作用が高いジャンプ発生後の10〜20営業日において、過去の推定共分散行列との乖離が大きく、また対数尤度が低下している(= 共分散行列が急変している)傾向を確認。 - 同期間中に、最小分散ポートフォリオ(MV)などリスクベース戦略の平均リターンが有意に低下しており、これらのイベントが資産の関係性を変え、運用パフォーマンスを悪化させるリスクを早期に示唆している。 考察 - 大枠の知見 : 領域間相互作用の高い同時ジャンプは、既存の資産相関構造と異なる動きを示す可能性が高く、その後の相関変化とリスクベース戦略パフォーマンス低下に繋がり得る。 - 人間の運用判断との連携 : いつ・何が原因でこうしたジャンプが起きたかを時刻ベースで把握できるので、ファンドマネージャが定性情報を参照して、迅速なポートフォリオ見直しを検討する材料になる。 (17) アート資産の分散投資効果の実証分析 森田 梨加, 町田 奈津美 (慶應義塾大学/パリ政治学院), 山本 康介 (慶應義塾大学), 中川 慧 (野村アセット/大阪公立大学), 星野 崇宏 (慶應義塾大学/理研AIP) アート作品を「アート資産」としてとらえ、伝統的な金融資産(株式・債券・為替・コモディティなど)に組み入れることで得られるポートフォリオ分散効果を検証することを目的としています。これまでアート資産のポートフォリオ導入効果に関する研究は数多く行われていますが、アート資産特有の流動性リスクや、価格変動の非対称性・長期依存性などを同時に考慮した実証分析は限られています。 データ アート資産 - リピートセールス回帰法を用いて算出される ArtDAI 社の月次アート指数を採用。 - アートの流動性に注目し、流動性の高・中・低の3種類に区分したアート指数を用意(High Liquidity Art, Middle Liquidity Art, Low Liquidity Art)。 伝統的資産 - 株式:S&P500、日経225、EURO STOXX - 債券:米国10年債先物、ユーロ(ドイツ)10年債先物 - 為替:米ドル/ユーロ、米ドル/円 - コモディティ:WTI原油先物、金先物 - 期間:1998年11月〜2023年12月の月次データ 分析 (1) 各資産の月次対数収益率を用いて、まずベクトル自己回帰(VAR)モデルを推定。 (2) その残差系列に対して、非対称性や長期依存を考慮した多変量GARCHモデル(GJR-GARCHやFIGARCHと、DCCもしくはADCCを組み合わせた合計6種類)を比較し、AICやSBCなどの情報量基準が最良となる「最適モデル」を選択。 (3) ボラティリティ(変動)どうしの連動を示す「ボラティリティ波及効果」や「条件付きクロス効果」を推定。 (4) 見積もった共分散行列を用いて、ポートフォリオの最適ウェイトとヘッジ比率を算出し、アート資産の分散投資効果を評価。 結果 1. ボラティリティ波及効果・長期依存 - 低流動性アート指数において、他資産からのボラティリティの影響が大きく(正の波及効果が統計的に有意)、アート価格変動が外部ショックを受けやすいことが示唆された。 - 一方、アート資産自体のボラティリティは長期的に持続しやすい(FIGARCHモデルの係数が有意に正)。アート価格の変動が一度高まると、比較的長い期間にわたってその影響が続く特徴が確認された。 資産間の動的相関(条件付きクロス効果) 「全体」「高流動性」「低流動性」のアート指数はいずれも、DCC/ADCCモデルの推定結果でプラスの条件付きクロス効果が確認された。これは市場ショックが発生した場合、アートと他資産の相関が一定程度上昇・変動しやすいことを意味する。 ただし、相関の“非対称性”は今回のデータでは大きくは見られず(ADCCの非対称係数が有意でなかった)。 ポートフォリオ最適ウェイト・ヘッジ比率 いずれの流動性のアート指数を組み込んでも、WTI原油先物、日経225、EURO STOXXなどがアート資産との組み合わせにおいて高いウェイトが得られやすかった。これらを組み合わせることでリスク低減が期待できる可能性が示唆された。 一方、債券(米国債、ユーロ債)や米ドル/円といった資産は、アートとのポートフォリオ内での最適ウェイトが相対的に小さくなる傾向が見られ、アートとの相性は低いと考えられる。 ヘッジ比率の点では、S&P500や日経225、EURO STOXXをロングしている投資家がアートを一定量ショートする(あるいは逆の立場で保有する)ことで、リスク低減が見込める可能性が示された。 考察と意義 - 流動性の異なるアート指数を用いることで、低流動性アートほど市場ショックの影響を受けやすく、ボラティリティが高まると持続しやすいことが明確になった。 - それでも、株式やコモディティの一部とは比較的低い相関を保つため、アートを組み合わせることによる分散投資メリットを得られる可能性が示唆された。 - 一方、流動性リスクも大きいため、特に低流動性アートの組み入れには注意が必要であることが読み取れる。 テキストマイニング・LLMs(5件) (18) 金融テキストを対象とした強弱を捉えることができるセンチメントモデル開発のためのデータセット構築方法の検討および分析 高野 海斗 (野村アセット) 目的 - 資産運用業務ではニュースやレポートなど多くのテキスト情報を分析する必要がある。これを効率化するために、テキストから市場の「センチメント(ポジティブ・ネガティブなどの感情指標)」を抽出し、投資判断に活用する取り組みが進んでいる。 - 従来の研究では「ポジティブ/ネガティブ/ニュートラル」の三値分類が中心で、より細やかな「強弱を考慮した連続的な値」を出力するモデルや、それを評価するデータセットが不足している。 データセットの構築方法 景気ウォッチャー調査の先行きコメント - 内閣府が毎月実施する景気ウォッチャー調査の「景気の先行き」に対するコメント(2000年1月〜2024年12月)を対象とし、5段階評価が均等になるよう100件をサンプリング。 人手と大規模言語モデルを組み合わせて絶対評価(Pointwise)・相対評価(Listwise)の2種類のラベル付けを実施し、相関係数や誤差から分析した。その結果、 相対評価(Listwise)の方が一貫した強弱を捉えやすい ことや、 GPT4系モデルは人間の平均スコアと高い相関を示す一方、スコアの水準感がずれる場合もある ことが明らかになった。また「コメント内の時間軸の混在」や「ポジ・ネガ要因の併存」がアノテーションを複雑化することを示し、金融におけるセンチメント定義の難しさと、その定義を活用目的に即して厳密化する重要性を指摘している。 (19) 景気ウォッチャー調査のデータセット構築と物価センチメント分析 鈴木 雅弘 (東京大学/日興アセット), 坂地 泰紀 (北海道大学) 景気ウォッチャーのデータを作成した https://github.com/retarfi/economy-watchers-survey データ - 2000年1月〜2024年5月までの景気ウォッチャー調査結果(現状・先行き) - 必要な列(地域/業種など)を整形し、最終的に 現状約31万件・先行き約33万件 を構築。 - 毎月公開される最新データを取り込み、GitHub Actionsを使いPull Requestで差分を管理→人手確認後マージ→Hugging Face Hubに自動アップロード。 (20) 決算短信を用いた業種ごとの年間レポートの自動生成 竹下 蒼空, 酒井 浩之 (成蹊大学) 投資判断を行う上で、業種別に市況動向や業績の変遷を把握できるレポートは有用である。しかし企業数・業種数が膨大であり、手作業でレポートを作成するには大きなコストがかかる。 決算短信中に記載される「業績要因文」を業種ごとに分類し、これらの文をもとにChatGPT-4oを用いて 年別かつ業種別の自動レポート生成 を試みる。 データと抽出 - 対象データ: 2022年分の決算短信9,933件。 - 抽出: 既存の研究(酒井ら)の手法を用いて、決算短信から「業績の要因が記載されている文(業績要因文)」を抜き出し、合計49,605文を得る。 業績要因文について - 業種:日本標準産業分類(小分類530)を採用 - テキスト埋め込みモデル:OpenAIの「Text-embedding-3-small」(ベクトル次元1,536)を用いて、業績要因文および業種名をベクトル化し、意味的な類似度(コサイン類似度)を計算。 - 3種類の分類手法を比較 1. 階層型クラスタリングのみ - 業績要因文49,605文をクラスタ数530に分割(Ward法・ユークリッド距離)。 - 各クラスタの平均ベクトルと業種名の類似度により小分類業種を割り当て。重複する業種は統合し、最終的に139業種を生成。 階層型クラスタリング + 最近傍法の併用 業績要因文の中に「部門」「セグメント」など5種類の単語を含むもののみでクラスタリング。 それを除く文はクラスタの平均ベクトルとの類似度が最も高いところへ最近傍法で割り当て。 重複業種を統合後、最終的に131業種。 最近傍法のみ 小分類530業種の業種名それぞれをベクトル化し、各業績要因文が最も類似度の高い業種へ直接割り当て。 文数が少ない業種を中分類レベルに統合し、最終的に435業種を生成。 評価方法 : 3手法のいずれでも生成された共通業種のうち5業種をランダムに選び、分類結果(業績要因文→業種)を人手で正解/不正解判定。 業績要因分の結果 : いずれも約85〜90%前後の精度 考察:業種推定精度 どの分類手法もおおむね高い精度(0.85〜0.87)で業種を推定可能。 ただし業種の粒度を細かくすると、より適切な業種があり得るケースが発生するなど、厳密な正解づけが難しい。 考察:レポート生成の質 実際の決算短信中の業績要因文を取り込むため、「売上」や「需要」への具体的な言及が含まれやすく、記事に近い内容の文が生成される。 比較手法(業種名のみ)だとこうした具体的文言が出ないことも多く、結果的に類似度が下がる。 一方、ChatGPTへのプロンプト次第で内容が左右され、またChatGPT-4oが一部の入力文を無視する場合もあるなど、生成制御には課題が残る。 本研究の自動生成手法は、決算短信ベースでの詳細な業種別レポートを効率的に作成する手段として有望であることが示された。 (21) LLMを用いた情報系企業のESG評価についての一考察 島津 雛子 (青山学院大学), 濱崎 良介, 大塚 浩 (富士通), 飯島 泰裕 (青山学院大学) 企業の ESG(環境・社会・ガバナンス) への取組みが年々重要になり、欧州では2024年から「欧州サスティナビリティ報告基準(ESRS)」に基づく情報開示が始まっている。 日本でも財務・非財務情報を統合した報告書(統合報告書)の発行が進み、その「トップメッセージ(CEOメッセージ)」は企業の価値創造ストーリーやESGへの姿勢を簡潔に示す重要な部分とされる。 しかし、トップメッセージを定量的に評価する手法はまだ確立されていない。本研究では LLM(大規模言語モデル) を用いて定量的かつ容易にESG評価する手法を探る。 ChatGPTの「MyGPT」機能を利用 - 専門的なLLMやプログラミング知識を要しない形で、ESGに特化したLLMを準備し、企業のESG分析を行う。 評価対象 : 情報系日本企業の10年分の統合報告書トップメッセージ(2013〜2024年) 評価方法 : - トップメッセージを E(環境) , S(社会) , G(ガバナンス) , O(その他) の4カテゴリに分類し、どのカテゴリの言及が多いかを可視化。 - ESGのフレームワークとして、 GRIスタンダード や ESRS基準 をChatGPTに知識として与え、「企業情報」と「ESG知識」の2つのデータベースを作成。 - トップメッセージ中の各文を4つのカテゴリに自動分類し、その正確性と企業のESG姿勢の変遷を分析。 1年分(2023年)のテスト - ChatGPTの分類結果と人手で付与したラベルを突合したところ、一致率は約98.2%と高い精度を示した。 - キーワード単純照合ではなく文脈からE/S/G/Oを判断していることが確認され、有用性が示唆された。 10年分の分析(2013〜2024年) - トップメッセージ各年の文章を分類し、ESG言及数の推移を可視化。 - 分析対象企業の特徴 - 年ごとのESG言及割合は一定の増加ではなく、経営体制の刷新やサステナビリティ戦略の導入タイミングで上下。 - 社会(S)への言及が他のE・Gより際立って多い。これは人的資本経営や社会的課題への積極的対応を示唆し、近年のトレンドを取り込みやすい企業と推察される。 - 一方で環境(E)への言及は少なく、同社が環境対応についてあまり言及・公表していない可能性が示唆される。 考察と意義 - 従来研究ではBERTやRAG等のLLMモデルをプログラミング込みで扱う事例が多かったが、本研究ではChatGPTのGUI機能とプロンプト設計のみで企業のESG言及を高精度に分類できることを実証。 - 投資家や企業関係者などが プログラミング不要 で簡便にESG評価に活用できる可能性を示した。 (22) LLMによる大量保有報告書における担保契約等重要な契約に関する情報の構造化 高橋 明久, 戸辺 義人 (青山学院大学) 「5%ルール」によって上場企業の発行済株式を5%以上保有した個人・法人は、取得から5営業日以内に大量保有報告書を提出する義務がある。 報告書には、保有株式数や取得目的、担保契約などの重要な契約情報が記載されるが、自由記述形式のため機械的な情報抽出が困難。 課題 : 多種多様な自然言語記述で記載される「担保契約等重要な契約」を自動的に抽出・分類し、構造化する手法が求められている。 目的 : 最新の自然言語処理技術(BERTやLLM)を用いて、大量保有報告書の自由記述欄から契約種別や契約内容を抽出し、構造化するための基礎的アプローチを確立する。 データ : 2023年1月〜12月にEDINETで公開された大量保有報告書990件から取得。 担保契約等重要な契約の記載がある778件を対象にし、そこに含まれる複数の契約文章を分割・整形して 合計1298文 の契約データを整備。 ラベル付け :契約種別の分類(貸借契約、担保契約、その他契約)、契約内容の抽出(契約相手方、契約株数、開始日、終了日)。すべて人手でアノテーションし、学習・評価データを作成。 契約種別の分類タスク - 東北大学の日本語BERTをベースにfine-tuning。3クラス(貸借/担保/その他)分類のクロスエントロピー学習を実施。 契約情報の抽出タスク(QA形式) - 「契約相手は?」「開始日はいつ?」「株数は?」などの質問形式を設定し、BERTの質問応答モデルを構築(SQuAD類似の枠組み)。 - 各質問に対し、文章内の該当箇所を抽出(開始位置・終了位置を予測)。 加えて、OpenAIのGPT-4oを使用し、上記の分類タスクを行うことも試した。 BERT および GPT-4o のいずれも、貸借契約/担保契約/その他契約の3クラス分類で Accuracy=100% の結果。 情報抽出タスク - 評価指標: QAタスクで一般的なExact Match(EM)とF1スコアを採用。 - BERTのQAモデル:項目によりばらつきが大きく、「契約相手方」抽出でEM=0.16と低調、「契約終了日」ではEM=0.62。F1スコアでも最大0.62程度に留まる。 - LLM(GPT-4o):全項目(契約相手方,株数,開始日,終了日)で EM=0.82〜0.92 と非常に高い。表記ゆれや文章構造の違いにも柔軟に対応し、高いスコアを達成。 機械学習1(5件) (23) 多様なテンプレートと合成データを用いた大規模言語モデルの業種区分予測における知識抽出 矢野 一樹 (東北大学), 平野 正徳, 今城 健太郎 (PFN) 金融業界ではLLMを活用したデータ分析や業務効率化が進んでいるが、日本特有の金融知識(例:JPXの業種区分)を適切に活用できるかが課題。 業種区分(JPXの33業種分類)は投資判断やポートフォリオ構築に重要な指標であり、LLMが適切に学習・活用できるかが問われる。 一般的なLLMは業種区分の知識を十分に学習・活用できていない(金融特化LLMであっても予測精度が低い)。 ファインチューニング(微調整)による知識抽出の最適な方法が未確立。 既存研究では知識を多様な表現で学習させることで精度向上が可能とされるが、最適なデータセット設計についての体系的な検証が不足。 目的 - LLMが持つ金融知識(特に業種区分)をより適切に抽出・活用するためのファインチューニング手法を探る。 - テンプレートを用いた多様な表現とルールベースおよびLLMによる合成データを活用し、業種区分予測精度を向上させる。 - パープレキシティと予測精度の関係を明らかにし、効果的なデータセット設計の指針を提供。 (24) 機械学習による企業業種分類の定量評価と応用 屋嘉比 潔 (大阪公立大学), 中川 慧 (野村アセット/大阪公立大学) 東証33業種分類やGICS(Global Industry Classification Standard)などの従来の業種分類は、企業の多角化、M&A増加、新興産業の誕生に伴い正確性が低下するという課題を抱えている。 Gradient Boosting Machine(GBM)を用いた機械学習モデルで、新しい業種分類を提案。 従来の業種分類と比較し、企業間の類似性をより正確に評価できるか検証。 機械学習ベースの業種分類が投資戦略(会計発生高アノマリー戦略)に応用可能かを評価。 データセット - 2001年~2018年の日本の上場企業データ(NEEDS-FinancialQUEST)。 - 特徴量: 財務指標(売上高、ROA、研究開発費比率、営業利益率、総資産回転率など)、市場データ(β、時価総額、株価変動率など) 目的変数 : 時価簿価比率(market-to-book ratio)を対数変換したものを用いる。 GBM(Gradient Boosting Machine)を利用し、企業間の類似度を学習。業種コードを基準とせず、財務・市場データをもとに動的に企業間の類似性を算出。 RMSE(Root Mean Squared Error)を用いて予測精度を測定。 (25) 機械学習を用いたクロスセクション予測における株価および業績シグナルの比較分析 小田 直輝 (慶應義塾大学), 中川 慧 (野村アセット/大阪公立大学), 星野 崇宏 (慶應義塾大学/理研AIP) 株式市場ではクロスセクション予測(複数銘柄のリターンを同時に予測する手法)が広く用いられるが、従来の手法ではリターンの非線形な変動や相互作用を十分に捉えられない。 従来の研究では、将来の株価リターンの相対順位をラベルとする「株価シグナル」が主に利用されてきたが、決算期には企業の業績情報が集中して開示され、市場の変動が大きくなるため、「 業績シグナル(企業業績や成長率の相対順位) 」が重要となる可能性がある。 株価シグナル :将来の株価リターンの相対順位をラベルとする(過去リターンを正規化)。 業績シグナル :次期の企業業績(売上高、営業利益、経常利益、純利益)およびその成長率の相対順位をラベルとする。 結論 - 業績シグナル単体は株価シグナルには劣るが、リスク抑制に有効 - 決算期以外では株価シグナルの方が優れた予測精度を示す - 決算期には株価シグナルと業績シグナルを組み合わせることで、予測精度が向上 - 最適な業績シグナルは「純利益」であり、特にリッジ回帰(RR)モデルで高パフォーマンスを示す (26) Multi-step Utility Lossを用いた深層学習モデルよる動的ポートフォリオ最適化 久保 健治 (東京大学/松尾研究所), 中川 慧 (野村アセット/大阪公立大学) ポートフォリオ最適化と機械学習の応用 - ポートフォリオ最適化は金融分野で重要な課題であり、従来の線形モデル(CAPMやマルチファクターモデル)では市場の非線形な特性を十分に捉えられない。 課題 - 既存の手法は「銘柄間の相関構造(クロスセクション)」と「時間方向の変動(時系列)」を別々に扱っており、両方を同時に考慮する手法は発展途上。 - 多くのポートフォリオ最適化手法は取引コストを考慮していないため、実際の運用環境ではパフォーマンスが劣化する可能性がある。 目的 - 取引コストを考慮した新たな損失関数「Multi-step CARA型損失関数(MS-CARA Loss)」を提案 - 時系列方向とクロスセクション方向を同時に考慮する「Dual Attention」モデルを導入 - S&P 500のデータを用いた実証分析で、従来手法よりも有効なポートフォリオ最適化が可能か検証 (27) ボラティリティ推定機能を持つ条件付き拡散モデルによる金融時系列生成 吉田 凌也 (東京大学), 平野 正徳, 今城 健太郎 (PFN) 金融市場の取引戦略の評価には金融時系列データが必要不可欠だが、過去のデータのみでは、未来の様々な市場環境を想定した戦略評価が難しい。そこで、シミュレーションデータを用いた戦略評価の重要性が高まっている。 これまでの金融時系列生成には主にGAN(Generative Adversarial Networks)が用いられてきた。近年は、拡散モデル(Diffusion Model)がGANよりも安定した学習と高品質なデータ生成を実現し、金融時系列の生成にも活用されている。 金融時系列の生成とボラティリティの推定を同時に行う条件付き拡散モデルを提案。 機械学習2(5件) (28) ベイズ推定における事前分布の作成にChatGPTを用いた暴落時の日経平均株価の予測 石井 成來, 堀田 大貴 (茨城大学) リーマンショック(2008年)やコロナショック(2020年)などのように、金融市場では大きな下落(暴落)が突発的に発生することがある。これらの暴落下での株価予測は、投資家のリスク管理およびリターン最大化において非常に重要である。 ChatGPTを利用してガウス過程回帰における事前分布を構築し、暴落時の日経平均株価を予測する新たなデータ分析の枠組みを提案する。 2024年の「植田ショック」を事例として、事前分布の生成やベイズ推定を行い、予測精度を検証する。 データ - 植田ショック(2024年8月)の日経平均株価 - 2024年7月31日の金利引き上げ発表後、市場が急落(3営業日で約20%下落)。 - 予測対象期間:2024年8月2日15:00~8月5日14:00(1時間足の終値)。 (29) 深層生成モデルによる金融市場の注文板の生成 高橋 友則 (総合研究大学院大学), 水野 貴之 (国立情報学研究所) 研究背景 株式市場など金融市場の構造を再現するシミュレーション手法として、高頻度取引データから「実際らしい」注文情報を生成できるモデルが注目されている。 従来は価格時系列データ自体の生成(GANなど)や、注文発生を表す点過程モデル(Hawkes過程)などが研究されてきたが、より直接的に「注文板全体を構成する注文の流れ(時系列)」を生成する方法の需要が高まっている。 モデルの概要 著者らは注文情報(新規or取消、売or買、注文形式、指値価格、数量)を文字列化(トークン化)し、自己回帰型言語モデルとして扱う手法を提案。 具体的には、 Transformer ベースの GPT アーキテクチャ 状態空間モデル (SSM) を応用した MAMBA アーキテクチャ この2種類をそれぞれスクラッチから学習した。 トークン化には、平仮名・片仮名・アルファベットの文字を組み合わせる独自の方式を用いている。これにより、整数値(指値価格・数量)とカテゴリ(新規/取消など)をまとめて「文字列」として符号化している。 データと実験 学習データとしては、2021年1月の東京証券取引所のトヨタ自動車株(銘柄コード7203)前場(9:00~11:30)のザラバに発生した注文情報を使用。 ザラバ中の売買・新規/取消・指値価格・数量などを連続するトークン系列とみなし、GPTとMAMBAを訓練。 生成結果として、実際の注文推移と類似した連続的な「売買注文の流れ」が再現可能であることを示している。 (30) 機械学習モデルを用いたシミュレーション分析:地方債市場における会計情報の寄与 原口 健太郎, 丹波 靖博 (西南学院大学), 池田 大輔, 阿部 修司, 大石 桂一 (九州大学) 日本の地方債市場を事例として、会計情報が債券金利(対東京都債のスプレッド)にどのような影響を与えているかを、機械学習モデルおよびシミュレーション的手法を用いて分析した研究です。従来の回帰分析(OLS)が非線形関係を捉えにくく、また機械学習モデル単独では「他の説明変数を固定してテスト変数を変動させる」分析が困難であるという課題を踏まえ、 仮想グリッドデータ と SHAP を組み合わせる新たなフレームワークを提案ています。 機械学習モデル(XGBoost)を訓練したうえで、そのモデルに仮想グリッドデータを入力することで、「一部の説明変数(テスト変数)だけを段階的に変化させ、他の説明変数(コントロール変数)を固定する」シミュレーションを可能にする手法を提案。さらに、モデルの解釈にはSHAPを用いることで、予測に対する各変数の寄与度合いを可視化する。 結果と考察 - 本研究では、「機械学習モデルで学習→仮想グリッドデータでシミュレート→SHAP で解釈」という枠組みを提案し、日本の地方債市場における会計情報の非線形かつ強力な寄与を実証した。 - 暗黙の政府保証があるとされる地方債であっても、財政状態の悪化は金利上昇に結びつくことを示唆しており、投資家が「クレジットリスク」をある程度織り込んでいる可能性がうかがえる。 - この枠組みは会計・ファイナンス以外でも「定式化が難しいが非線形関係が疑われる」現象分析に広く応用できる。機械学習モデルの評価・精度検証の際には、試行回数を増やし、平均収束を確認する手順が重要である。 (31) KANベース自己符号化器を用いた国債イールドカーブのファクターモデルの構築 水門 善之 (野村證券/東京大学), 米田 亮介 (野村證券) 国債イールドカーブを表す三因子モデル(レベル・スロープ・カーヴァチャー)を、自動符号化器(オートエンコーダ)の枠組みで構築し、さらにその中核モデルとして Kolmogorov-Arnold Network (KAN) を活用する手法を提案しています。 国債イールドカーブ: 短期から超長期までの国債利回りを連続的につないだもの。マクロ経済や需給要因などが複合的に影響し、日々変動する。 三因子モデル: イールドカーブは大きく「水準(レベル)」「傾き(スロープ)」「曲率(カーヴァチャー)」の3種類の因子(ファクター)によって概要を捉えられるという考え方が広く知られている(例:Nelson-Siegelモデル)。 Kolmogorov-Arnold表現定理: 多変数の連続関数を一変数関数の重ね合わせで表現できるという定理。KAN は、この重ね合わせを多層化して表現力を高めたネットワーク構造。 自己符号化器構造 - 入力層: 2, 5, 7, 10, 15, 20年など主要年限の国債利回り(週次データ)。 - 中間層: 3ノード(圧縮表現によりイールドカーブを3次元空間に写像)。 - 出力層: 元のイールドカーブ(入力と同じ次元)。 - 学習データ: 1992年7月~2024年12月までの週次国債利回り。 三因子との対応 - 学習を終えた中間層の3ノードを時系列で可視化すると、それぞれ「水準(2年債利回りに近い動き)」「傾き(長短金利差に近い動き)」「曲率(バタフライスプレッド等に対応)」を表していることがわかる。 - 従来のニューラルネットワークによる自己符号化器でも同様の結果が得られるが、KAN を使うことで中間層のノードを計算する関数の構造を可視化し、その解釈をより直接的に確認できる。 KAN固有の利点: 関数形の解釈 - KAN は「入力層 → 中間層」「中間層 → 出力層」を結ぶ近似関数をスプラインなどで明示的に定義する。 - 実際に得られた関数形を調べると、水準ノードは全年限の金利とほぼ比例関係、傾きノードは短期と長期で符号が反転、曲率ノードは中期と超長期に対して反転、といった形が確認できる。 - これらのパターンが「実際のイールドカーブにおける水準・傾き・曲率の変曲点」と整合しており、モデルの解釈性を高める。 まとめ 提案手法の有効性 KAN を自己符号化器として用いることで、日本国債イールドカーブの動きを自動的に3ファクター(レベル・スロープ・カーヴァチャー)に分解できる。 従来のオートエンコーダでも同様の因子抽出は可能であったが、KAN の「関数形状を直接確認できる」という特長により、学習後の解釈がさらに明確になる。 今後の応用可能性 イールドカーブだけでなく、他の金融時系列や複数の説明変数をもつ現象に対しても、「KANベース自己符号化器を使ってファクターを抽出し、そのファクターと入力との関数関係を可視化する」手法が適用できる可能性がある。 (32) 有価証券報告書と統合報告書を活用したESG投資のためのオントロジー構築と生成AIによる情報抽出 大堀遼介 (ulusage) 企業が開示するサステナビリティ関連情報(有価証券報告書、統合報告書、ニュース記事など)をもとに、環境・社会・ガバナンス(ESG)に関わる情報を一元的に活用するためのナレッジグラフと質問応答(QA)システムの構築手法を提案しています。 有価証券報告書や統合報告書などに分散するESG関連情報をナレッジグラフとして統合し、さらにGNN・LLMを活用して知識補完とQAを高度化する方法論を示しました。構築されたGraphRAGシステムにより、ESG投資のための情報を一貫して検索・推論し、根拠の明確な回答を提供できる基盤技術が確立されつつあるといえます。 雑感 上記の通り、テキスト情報の扱いが増加しています。LLMの出現以降、その流れが加速しているように思います。 この研究会の歴史をまとめた発表もありましたが、規模が大きくなり2日間の開催が定着してきましたね。注目度がより高まっていることがわかります。
アバター
Insight Edgeのデータサイエンティストの山科です。 今回はタイトルにもある通り、画像に対する異常検知結果を大規模言語モデル(LLM)で解説させることで説明性を付与できるか検証を行いましたので、その結果について記載したいと思います。 なお、本内容は先日、長崎で開催された自然言語処理学会(NLP2025)でも発表した内容( 自然言語での異常解釈:LLMを用いたAI説明モデルの提案 )となっています。 目次 はじめに なぜ異常検知タスクで説明性が必要なのか 提案アプローチ 実験 まとめ はじめに 異常検知は製造業や医療分野で不可欠であり、迅速かつ正確な判断が求められていますが、その出力が抽象的で理解しづらいことがあります。そこで、画像処理アルゴリズムによって検知された異常をLLMを用いて自然言語で説明ことで、より解釈しやすくする新しいアプローチ、言語駆動型説明可能AI(Language-Driven Explainable AI)を提案し、異常検知結果の信頼性と透明性の向上を図ります。また、その出力が実業務に有用であるかも検証しました。 なぜ異常検知タスクで説明性が必要なのか 近年、深層学習技術の進展に伴い、画像認識における異常検知の精度は飛躍的に向上しています。特に製造業や医療分野において、製品の欠陥検出や病変の早期発見など、様々な応用が期待されています。しかし、異常は一般的に発生しにくい事象であるため、正常データに比べて異常データの収集に課題があります。また、異常のパターンは多様で明確に定義することが難しく、各異常は異なる特徴を持つため、一貫して検出するためのモデル設計も困難です。さらに、ラベリングが複雑で誤りが発生しやすく、アノテーションコストが高いことも課題となっています。このため、異常検知の精度向上に向けた多方面での研究が引き続き行われています。 また、既存の深層学習モデルはブラックボックスであることが多く、異常と判断した根拠を人間が理解することが困難なケースも見られます。このため、現場での実運用の場面では、検知結果を信じてプラントを停止すればよいのか、あるいは、大したことのない異常なのでそのまま流せばよいのか、といったように最終決定をする作業員が判断に迷うことがあります。このため、AIの判断に対する信頼性の担保や、さらなる性能向上のために、AIの意思決定プロセスを説明可能なものにする Explainable AI(XAI、説明可能なAI)技術 が注目されています。 しかし、XAIの手法で生成される説明が専門家でないと解釈が難しい場合や、説明が多すぎて重要な情報が埋もれたり、逆に詳細に欠けることがあります。これにより、適切な情報量のバランスを取ることが難しいという課題があります。 一方で、近年のLLMの発展はXAIの可能性を大幅に広げています。例えば、画像キャプション生成タスクのように、画像に対する異常検知結果をLLMの入力として説明文を生成することで、画像内の異常の理由を人間が理解しやすく自然な言葉で説明できます。さらには、異常検知結果が誤っていた場合にもその間違いを正すことで異常検知精度の向上が期待できます。また、異常の種類や発生箇所から発生要因をフィードバックすることで、異常検知後の作業の優先順位を指示することも期待できます。 提案アプローチ 説明性付与のアプローチとして、画像に対する異常検知結果を LLM へのインプットとすることで説明性を付与する言語駆動型説明可能 AI(Language-Driven XAI)を提案します。 異常検知モデルとしては、例えば、多クラス分類やセグメンテーション、物体検知モデルなどを想定しています。 提案アプローチ 実験 実験設定 データセットとして、産業用異常検出のベンチマークとしてよく評価されているMVTec ADを用いました。 MVTec AD は、合計5354枚の15のクラスの画像で構成されており、各クラスは特定の製品に対してさまざまな欠陥タイプを持つ異常データとなっています。今回は、テストデータである1725枚を対象に評価しました。 異常検知モデルとしては、正常か異常かの二値分類、欠けや印刷ミスといった複数の異常の種類を分類する多クラス分類、画像内の異常箇所をピクセルレベルで評価しヒートマップで表すセグメンテーション、画像内の異常箇所をバウンディングボックス(bbox)で示す物体検知、の4種類を適用して、これらの手法によるキャプション生成の結果を比較評価します。 今回検証に用いた各異常検知種法のモデルは以下の通り。なお、各パラメータに関しては、誤検知や検知漏れの結果に対しても評価するため、ある程度ランダムとしました。 異常検知モデル また、モデル毎に出力される結果も異なるので、モデル毎に下表の通り指示文(プロンプト)を調整しています。 また、LLMにはgpt-4o(OpenAI)を用いました。 モデル毎のプロンプト例 評価としては、LLMで生成された説明のキャプションの正確性、個数や位置、大きさ、形状などの詳細度、異常のバリエーションに対する柔軟性、を評価項目としました。キャプションの生成結果は定量的に評価する手法も提案されていますが、ここでは、設定した5段階の 評価スケールを用いて人間による定性的な評価としました。「正確性」と「詳細度」は一枚一枚採点し、その平均点を算出し、「柔軟性」は異常のバリエーションに対する評価となるので各手法での全結果を鑑みて採点することとしました。 実験結果 二値分類 まず、異常検知モデルとして二値分類モデルを用いた場合について示します。入力画像と異常検知結果、生成されたキャプション例は以下の通りです。亀裂と割目から異常であることを正確に説明できており、また、保存状態にも言及することができていますが、位置や傷の数などについての説明はなく詳細度はやや低い結果となりました。 二値分類モデル結果例 多クラス分類 次に多クラス分類モデルを用いいた場合の結果を示します。入力画像と異常検知結果、生成されたキャプションは次の通りです。 多クラス分類モデル結果例 異常の種類としてはsqueeze(へこみ)が正しいのですが、crack(割れ)と誤分類してしまったケースになります。しかし、生成されたキャプションでは、右側に凹みがあることを説明しており、誤判定である可能性があることを説明してくれています。また、「右側」や「オレンジ色の部分」といっ位置の情報も示されており詳細度が高く、ユーザーも理解がしやすく、検知精度向上へのフィードバックにも活用が期待できる結果を得られました。 セグメンテーション 次にセグメンテーションモデルについてです。セグメンテーションモデルへの入力画像と異常検知結果は次のようになりました。 セグメンテーションモデルの入出力 見えにくいですが赤枠の箇所に縦のほつれがありますが、セグメンテーション結果では、ほつれ付近の異常度が高くなっているものの左上と左下が特に異常度が高い結果となっています。LLMへの入力画像と生成されたキャプションを見ると、ほつれだけでなく色ムラや擦り傷の可能性についても言及できていますが、ヒートマップで元の画像が判断しにくくなっているためか、上から下にわたってほつれがあることまでは言及できませんでした。しかし、異常度が高い箇所以外にも言及できており詳細度は高い結果と言えます。 セグメンテーション結果例 物体検知 物体検知結果と生成したキャプションの例を示します。異常箇所2箇所のうち、1箇所を検知漏れしているのですが、キャプションではもう1箇所についても異常であることを言及してくれています。 検知できている異常箇所に対しては、テクスチャ、色、形の違いなど、様々な要因から異常の理由を詳述しており、検知漏れの箇所に関しても同様の要因から異常であることを言及しています。生成されたキャプションは異常の理由を多角的に説明できており、正確性と詳細度が高い結果となりました。 物体検知結果例 実験結果まとめ 他のクラスなど全テストデータに対して検証を行った評価結果は下表のとおりです。 評価結果 二値分類や多クラス分類では異常画像をそのまま入力としているため か、より詳細な説明をしやすく、また、誤検知や誤分類の際に訂正するようなキャプションを生成できるケースが多く見られました。異常の種類や発生要因についても言及できており、全体的に高い評価結果となっています。セグメンテーションでは、正しく検知できている場合は異常箇所の位置や個数なども詳述できているケースが多く得られました。しかし、ヒートマップを重ねているためか異常の種類について詳細に説明することが難しく、また、ヒートマップがあることによって検知漏れしている場合に検知漏れ箇所を言及できているケースも少なく、全体的にやや劣る結果となりました。物体検知では、異常箇所の位置や個数に関して言及するケースが多く、検知漏れしている場合でも異常箇所を指摘できていたりと、正確性、詳細度、柔軟性とも高いキャプションが生成されやすい結果がえられました。しかし、bboxによっては異常箇所と重なってしまい、異常の種類を間違えてしまったり、検知漏れしている場合に、bboxが無いのにあるような説明をしてしまったりと、プロンプトの改善などが必要なケースもいくつか確認できました。 しかしながら、いずれの異常検知手法においても正しく検知できている場合には正確性、詳細度 が高いキャプションが生成できており、誤検知や検知漏れしている場合にも訂正したり、保存や輸送状態に起因するなどと言った異常の発生した要因についても言及したりと、詳細かつユーザビリティが高いキャプションが生成でき、本アプローチの有効性を確認することができました。 まとめ 本記事では、画像に対する異常検知結果に説明性を付与するLanguage-Driven XAIを提案し、産業用異常検出のベンチマークとしてよく用いられているMVTecADを対象に評価を行いました。異常についての説明だけでなく、発生要因や誤検知時に訂正できることも確認でき、よりユーザビリティの高い検知システムに寄与できることを確認できました。今後は、以下についても引き続き検証を進めたいと思っています。 複数の異常検知結果を入力とすることでより安定性の高いキャプションの生成可否 マニュアルや事故事例集をRAGに活用して、作業工程へのフィードバックや、設備へのメンテナンスへのフィードバックの可否 実データを対象とした検証 参照 [1] 自然言語での異常解釈:LLMを用いたAI説明モデルの提案 https://www.anlp.jp/proceedings/annual_meeting/2025/pdf_dir/Q5-9.pdf [2] 私のブックマーク「機械学習における解釈性(Interpretability in Machine Learning)」 https://www.ai-gakkai.or.jp/resource/my-bookmark/my-bookmark_vol33-no3/ [3] The MVTec anomaly detection dataset https://www.mvtec.com/company/research/datasets/mvtec-ad
アバター
はじめに こんにちは、Insight Edgeの関です。今回は、2025年2月21日(金)に大手町で開催された、住友商事グループ(SBU/事業会社、コーポレート)向けのInsight Edge主催リアルイベントについてご紹介します。 はじめに イベント開催目的 イベントコンテンツ詳細 ノベルティ イベント開催結果 まとめ イベント開催目的 Insight Edge(以下、IE)のミッションである「技術の力で世界を“Re-Design”する」から、「Re:design」をキーワードとして、IEの技術力を体感していただくイベントを開催しました。今は、「デジタルでともにNo.1事業群へ」をテーマに、事例展示・体験ブースやトークセッションを実施。DX推進の具体的なイメージをアップデートするとともに、その実現に向けた武器の一つとして、IEの伴走事例や最新技術の動向、さらにはIEが提供する価値や魅力を持ち帰っていただくことを目的としました。 イベントコンテンツ詳細 イベントでは、大きく以下3つのコンテンツを企画・実施しました。 コンテンツ①「事例展示・体験ブース」 Insight Edgeが過去1年間に取り組んだ案件の中から、7つの事例をパネル展示でご紹介しました。また、一部の案件ではデモを通じて、実際に触れて体感していただく機会も設けました。 展示企業 事例タイトル メディア系 データ活用組織の立ち上げ・育成・実践 エネルギー系 電力需給管理システム開発を通じた包括的支援 弊社 事業改善のタネを発見する!顧客の声・従業員の声分析ツール【 Voiceek 】 小売販売系 AIを活用した値引システムの開発 通信系 自律的にデータサイエンスタスクを遂行するマルチAIエージェント開発 介護系 デザインで介護事業に新たな価値を 投資系 生成AIを活用した投融資意思決定支援ツールの開発 コンテンツ②「トークセッション」 弊社CEO小坂らで住友商事グループのDXについて語るオープニングセッションを皮切りに、4つのトークセッションを実施しました。プロジェクト実担当者とのパネルディスカッションや、弊社データサイエンティストによる生成AI情報トレンドセミナーを実施しました。 キーワード 概要 住友商事グループのデジタル 本イベント開催にあたってのご挨拶と、住友商事グループのデジタルによるさらなる成長に向けたKeynoteセッションをお届け。 生成AI最新動向 現在の生成AIの流行に惑わされることなく、AIエージェントの背景を正しく理解し、情報を適切に評価するための基礎知識をご提供。 デザインと事業開発 UI/UXデザインが介護における事業開発のスピードを大きく加速させた事例を紹介。総合商社におけるデザインの新たな可能性を探った。 データ活用の全社推進 メディア系の事業会社におけるデータ活用のプロジェクトの全体像とその成果を紹介。コアメンバーが登壇しパネルディスカッションを通しデータ活用の最前線をお届け。 自律型AIエージェント データサイエンスタスクを自律的に遂行するAIエージェントの開発プロジェクトの詳細とその成果を紹介。最新の技術トレンドやAIエージェント活用に向けたヒントをお届け。 コンテンツ③「懇親会」 今回のイベントでは、事例展示会場にて懇親会も開催しました。参加者の皆さまには、展示事例を見ながらフランクに会話を楽しみつつ、DX推進の取り組みに関する情報交換の場としてご活用いただきました。また、定時中にご都合がつかない方にも参加いただける貴重な機会となりました。 ノベルティ Insight Edgeのロゴやイベントキービジュアルをあしらったノベルティ(トートバッグ、スクリーナークロス)も配布しました。 イベント開催結果 イベント当日は、延べ257名の方にご来場いただきました。事例展示では、「デモを見ながらの説明が分かりやすかった」「活用できるユースケースを持ち帰れた」といった声が寄せられました。 また、トークセッションでは、「事例を詳しく知ることができた」「AIの知識がなくても理解しやすかった」と好評をいただきました。 さらに、懇親会では「IE社員との交流を通じて、技術力だけでなく、人柄や仕事への情熱も伝わった」との感想も寄せられました。 まとめ 今回の記事では、Insight Edge主催のリアルイベント 「Insight Edge Re:design 2025 〜デジタルでともにNo.1事業群へ〜」 についてご紹介しました。IEでは「みんなでやる」というバリューを大切にしており、準備段階からイベント当日まで、その精神を体現できる場となったと感じています。 IEの提供する技術にご興味のある方、またはIEでの働き方に関心をお持ちの方は、ぜひお気軽にご連絡ください。 最後までお読みいただきありがとうございました。
アバター
はじめに こんにちは。2023年12月からInsight Edgeに参画したData Scientistのカイオと申します。 入社してから幅広い分野のAIや機械学習だけでなく、API構築やクラウドと関わり海外出張までする機会があって非常に感謝しています。 最近、LLMを使ってPPTXを生成する案件に携わり得た知識を共有しようと思ってこの記事を書きました。 目次 PPTXファイルの構成 PythonによるPPTXライブラリ(python-pptx) わかった課題 まとめ PPTXファイルの構成 皆様ご存知だと思いますが、PowerPointが世界一使われている発表資料です。OpenOffice等のオープンソースアプリも存在しますが人気度がそこまで高くありません。 PowerPointは2007においてPPTからPPTXフォーマットに変わってその中身の仕様は大きく変わりました。中身は非常に複雑なため短いブログに書き切れません。そのため、今回はスライドレイアウトとプレースホルダーにフォーカスします。 PythonによるPPTXライブラリ(python-pptx) Pythonを使ってPPTXを操作したいならpython-pptx一択です(内部のXMLを直接いじるのもできるがおすすめできません)。 PPTXの使える機能は一部しかできませんが結構パワフルなツールです。 もちろん新規ファイルの作成だけでなく、既存のファイルを開いたり編集できたりします。 ただ、もっと複雑な処理(スライドのコピー等)はpywin32でしかできなくて環境がWindowsに限られる場合があります。 わかった課題 1:スライドレイアウト 普段あまり触ることはないかもしれませんが、PPTX発表に少なくとも1つのスライドマスターが存在します。 マスタースライド デフォルトではスライドマスターに11枚のスライドレイアウトが存在します。 1つのスライドレイアウトに複数のプレースホルダーがあってそれぞれ番号が振ってあります。以下は「Two Content」レイアウトの一例です: Two contentプレースホルダー プログラムで自動的に書き込む場合、次の課題があります。 どのプレースホルダーが左でどれが右か番号でのみわからない。 「真ん中の左のプレースホルダーに書き込んでください」のような指定ができない。 更に、ユーザや会社によってスライドレイアウトを編集したい場合があります。試しにプレースホルダーを2つ追加してみましょう: Two content編集後 気づきましたか?右と左が逆になりました。 それは私が最初に右側のプレースホルダーを作ったからです。 更に、右のプレースホルダーを削除しても番号は変わりません。 他人が作ったスライドレイアウトは見た目が同じでも過程が違うとPythonのコードも違って非常に手間がかかります。 わかった課題:一番ご理解いただきたいのは、LLMはその配置情報を知っていないことです。カスタムのレイアウトでLLMの出力をそのまま利用すると分かりづらい発表になるかもしれません。 2:日本語対応 今回、LLMで生成されたスライドを紹介しますが上記の課題もあってデフォルトのレイアウトに限ります。 任意の話題についてプレゼンファイルをこれから作ります。3つのステップで進めてみます: ユーザが入力したスライド枚数とテーマについてページごとの要約をLLMに依頼する def create_presentation_summary (user_theme: str , num_of_pages: int ) -> list [ str ]: system_prompt = f """You are an assistant for helping users create a PowerPoint presentation. Based on the theme on user input, return a summary of what each page of the slide should talk about. Therefore, your response should be a list of JSON strings. No matter what the user input is, the response should contain {num_of_pages} pages. You should write several sentences for each page. The language of your response should be the same as the user input. """ messages = [{ "role" : "system" , "content" : system_prompt}] messages.append({ "role" : "user" , "content" : user_theme}) スライド1枚の要約から適切なスライドレイアウトを選択する def get_appropriate_layout (summary_list: list [ str ]) -> list [ str ]: summary_list = json.dumps(summary_list, indent= 4 , ensure_ascii= False ) system_prompt = f """You are an assistant for helping users create a PowerPoint presentation. The user input will be a list of JSON strings, where each string represents a summary of each page of the slide. Based on the summaries, return a list of JSON strings, where each string represents the most appropriate layout for each page. The response should contain the same number of pages as the input. The language of your response should be the same as the user input. The layout options should be the ones available by default in PowerPoint. """ messages = [{ "role" : "system" , "content" : system_prompt}] messages.append({ "role" : "user" , "content" : summary_list}) スライドレイアウトと要約から箇条書きの中身をLLMが出力し、python-pptxを使って実際にPPTXファイルを作成する def get_placeholder_content (summary, layout): slide = prs.slides.add_slide(slide_layouts.get_by_name(layout)) num_of_placeholders = len (slide.shapes.placeholders) system_prompt = f """You are an assistant for helping users create a PowerPoint presentation. The user input will be a summary of the contents of a slide. The layout is {layout}. Based on the summary and layout, return the content that should be placed in each placeholder of the slide. The response should be a list of JSON strings, where each string represents the content of a placeholder. Each placeholder can have multiple paragraphs of text. Each paragraph should be separated by a newline character. There are {num_of_placeholders} placeholders in the slide layout. The first placeholder is the title placeholder. """ messages = [{ "role" : "system" , "content" : system_prompt}] messages.append({ "role" : "user" , "content" : summary}) response = openai_client.beta.chat.completions.parse(messages=messages, model=azure_deployment, n= 1 , temperature= 0 , response_format=StringList).choices[ 0 ].message.content response = json.loads(response)[ 'strings' ] for i, content in enumerate (response): for paragraph in content.split( " \n " ): p = slide.shapes.placeholders[i].text_frame.add_paragraph() p.text = paragraph 実行してみたらこんな結果になりました: 日本語フォント フォントサイズが大きすぎて大幅に枠を超えてしまいました。python-pptxにfit_text()関数があって、枠に入るように調整してくれるのですが日本語だとエラーになります。 色々頑張って調べましたがどうしても解決できなくてとりあえずフォントサイズを手動で小さくしてみました。 ところで英語のみだと以下のように上手くフォントサイズを調整してくれます: 英語フォント つまり、日本語対応がそこまで追いついていなくてLLMの出力には気をつけていただきたいのです。LLMに文字数や文章の長さを制限するのをお勧めします。 3:フォーマット指定 翻訳前 今回は少し違うタスクをやってみましょう。デフォルトのレイアウトだけだと制限が多すぎるので入力した 任意 のPPTXファイルのテキストをLLMで翻訳して同じ配置で出力してみます。 固定のプレースホルダーだけでなく、後で追加されたTextBoxにも対応したいのでshapesを使う: for shape in prs.slides[ 0 ].shapes: if shape.has_text_frame is True : shape.text_frame.text = translate(shape.text_frame.text) 翻訳後 色やボルド情報はもちろん、フォントサイズまで勝手に変わりましたね。 実は、デフォルト以外の形式情報はrunという変数の中に格納されています。 1つのtext_frameに1つ上のparagraphがあり、1つのparagraphに複数のrunがあります。 異なるrun 翻訳や要約など、文脈が必要なタスクはLLMに依頼するとき以下の方法のどれかを選ばないといけない: フォント情報を諦めてデフォルトのままにする(難易度が低い) JSON化して、paragraph全体の情報を渡すのと同時にrun毎の情報も渡す(難易度が高い) 2を実装するとしたら以下のよう構造化して渡すといいです(あくまで一例): [ { " text ": " a ", " color ": [ 255 , 0 , 0 ] , " bold ": true , " italic ": false , " underline ": false } , { " text ": " r ", " color ": [ 255 , 255 , 255 ] , " bold ": false , " italic ": true , " underline ": false } , { " text ": " t ", " color ": [ 0 , 0 , 255 ] , " bold ": false , " italic ": false , " underline ": true } ... ] そして、リスポンスを以下のように指定する: [ { " text ": " 人 ", " color ": [ 255 , 0 , 0 ] , " bold ": true , " italic ": false , " underline ": false } , { " text ": " 工 ", " color ": [ 255 , 255 , 255 ] , " bold ": false , " italic ": true , " underline ": false } ... ] もちろん、ChatGPTの構造化出力を使うと便利です。 しかし、複雑な処理になればなるほどLLMが細かいミスをする可能性が増えます。 まとめ このように、LLMを活用してパワポファイルの自動生成について述べました。PPTX形式は色々な制約があり、ある程度フォーマットにこだわりがなければLLMを上手く活用できます。 この記事に書いた内容はユーザがある程度形式をコントロールしたい想定です。python-pptxのコードまでLLMに生成させる方法も存在して こちら の記事は参考になります。
アバター
目次 はじめに PoCとアーキテクチャ PoCと本番システム開発の違い アーキテクチャとは PoCにおけるアーキテクチャ設計 品質(Quality)観点での検討 機能適合性 使用性 互換性 信頼性 セキュリティ 保守性 保守性:再利用性 保守性:解析性 保守性:修正性 保守性:試験性 保守性:モジュール性 性能効率性 移植性 品質特性に対するまとめ コスト(Cost)・納期(Delivery)も考えると 既存システムからの流用 共通機能の整備 まとめ はじめに こんにちは、Insight Edgeでエンジニアをしています伊藤です。 「これよくできているな」と言ってもらえるような設計・仕組みを目指して日々のエンジニアリングに取り組んでいます。 今回の記事では私の好物であるシステムアーキテクチャについてPoC(概念実証)の観点から考察してみたいと思います。 Insight Edgeは最先端技術をビジネスに活かすための技術者集団です。 そのため、プロジェクトではPoC(概念実証)としてプロトタイプを作成し、実際の現場・ユーザに試していただくことが多いです。 PoCをすることによって 想定しているテクノロジーの使い方で実際にビジネスを変えることができるか 役立てるために埋めなければいけないギャップは何か 適切にギャップをカバーすることで実際に使えるものができるか といった観点を確認し、最終的にどんなシステムを作るべきかについての学びを得た上で次のステップへと進みます。 本記事では、本番システムの開発とは異なるPoCならでは特性を品質面を中心に整理しつつ、アーキテクチャに求められる要素について考えてみたいと思います。 PoCとアーキテクチャ PoCと本番システム開発の違い PoCの実装もシステム開発の一種であるとはいえ、本番稼働するシステムの開発とは異なる面があるので簡単に整理しておきます。 下記はInsight Edgeでのケースですので、組織やプロジェクトによって状況は異なるでしょう。 PoC 本番システム開発 目的 どうすれば効果的なシステムになるかの学びを得る ビジネスに対して効果を出す 優先事項 早く検証しフィードバックを得る 費用対効果 利用期間 数週間〜数ヶ月 数年 運用する人 Insight Edge内のPoC開発メンバ 納入先の担当者 アーキテクチャとは アーキテクチャとは、あるシステム(広義のシステム=系)を ある観点 から見た場合の 構成要素 ・ 要素同士の関係 をモデル化して表現したものです。 本記事では、アプリ内やインフラ構成だけでなく、開発標準やルールといった開発プロセスも含んだ範囲をアーキテクチャとして扱っています。 PoCにおけるアーキテクチャ設計 ソフトウェアシステムのアーキテクチャを設計する目的についてはさまざまな捉え方がありますが、 今回はプロジェクトのQCD(Quality=品質、Cost=コスト、Delivery=納期)の観点から以下の位置づけとしておきます。 システム開発・運用・保守において、 Qualityの最大化、Costの最小化、Deliveryの迅速化を目的にアーキテクチャを定義すること PoC作成もシステム開発プロジェクトの一種ですので、QCDを考慮する必要があります。 PoCならではの考慮事項があるか、それはアーキテクチャを検討する際にどういった影響があるかをQuality面を中心に検討してみます。 品質(Quality)観点での検討 システムの製品品質についてはISO/IEC 25000シリーズ "SQuaRE" による定義がありますので、これをベースを検討します。 SQuaREの詳細についてはIPA(情報処理推進機構)の 「つながる世界のソフトウェア品質ガイド」 などを参照していただくとして、ここでは概要だけ紹介します。 ※IPA:「つながる世界のソフトウェア品質ガイド」より抜粋 それぞれの特性の内容は以下のようになります。 品質特性 説明 機能適合性 必要な機能を十分に、正しく提供できるか 性能効率性 システム側のリソースを効率よく使えているか 互換性 他システムと円滑に連携できるか 使用性 ユーザが容易に理解・操作できるか 信頼性 安定して動作し、障害から迅速に回復できるか セキュリティ 不正利用・情報漏洩の防止・追跡ができるか 保守性 変更や修正が容易に行えるか 移植性 異なる環境へ容易に適用・移行できるか それでは、それぞれの品質特性についてPoCの場合を掘り下げてみます。 説明の都合上、取り上げる順序は上記から入れ替えています。 機能適合性 要求された機能を十分に・正しく提供できるか、という観点での品質特性です。 以下の3つの副特性に分けて考えることができます。 副特性 説明 機能完全性 必要な機能が備わっているか 機能適切性 機能・プロセス・手順が無駄のないものになっているか 機能正確性 正確に処理・出力しているか PoCでは 機能完全性と機能適切性は、機能に過不足がないかという観点の副特性なので、 PoCでは必ずと言っていいほど検証の対象になります。 また機能正確性はPoCの目的により優先度が変わる副特性ですが、 Insight EdgeではLLMや数理モデルを用いたPoCが多いため 「実用上十分な精度を出せるか」という検証は優先度を高くすることが多いです。 しかし今述べた2点については本番システムの一部を切り出して検証しているだけで PoCだから求められる特性というわけではありません。 機能の面で考に、本番システムにはなくPoC開発時だけ求められるものとしては、 ユーザがどのような操作をしたかを後から確認できるようにする 操作後にテストユーザがフィードバックを入力できるようにする といったものが考えられます。 これらの機能が過不足なく備わっているかどうかが、PoC環境ならではの機能適合性といえるでしょう。 アーキテクチャへの影響 上記のPoC環境としての機能適合性を満たすため、アーキテクチャとしては UIフレームワークに操作トラッキングの仕組みを組み込んでおく WebAPIアクセス時のURLやパラメータを自動的に記録する 操作記録用のログストレージを通常のログとは別に用意する といった基盤機能の検討が必要になります。 使用性 ユーザが容易に理解・操作できるかという観点です。 PoCでは 使用性は機能適合性と並んで、PoCで頻繁に検証・フィードバックのポイントとなる観点です。 ただし使用性も「本番システムを設計する際の検討事項を一部抜き出し/先取りして検証する」という性質のものなので、PoC時のみに求められる要素は少ないと考えています。 PoC開発時には使用性分析のために、ユーザ操作やフィードバックを記録する機能が欲しくなると思いますが、それらの機能が備わっているかは、前項の機能適合性の観点に含まれます。 互換性 他システムと円滑に連携できるかという観点です。 PoCでは PoC用のシステムでは、本番システムで予定してる外部連携のうち、検証に必要な部分だけを実装しますが、 テストユーザが多い場合や組織内の権限情報を検証に使いたい場合は、PoC時のみ特定の認証基盤とのユーザアカウントの連携が必要になることがあります。 外部の認証基盤を利用する前提でも、開発者としては外部の認証基盤を使わずに、スタンドアロンでユーザ作成・権限設定・ログインができる仕組みを設けて、素早い動作確認等ができるようにしたくなると思います。 PoCの場合はそのようなバックドア的な仕組みを設けることが許容(というよりも推奨)されるという点も本番システム開発と異なる点です。 その際には、外部認証基盤とスタンドアロンの認証機能がお互い干渉せずに使えるか、も互換性という観点で検討すべき品質となります。 アーキテクチャへの影響 外部のアカウントをテストユーザとして利用する場合は、Microsoft Entra ID(旧 Azure Active Directory)やその他のOAuth2/Open ID Connect対応サービスとの連携を組み込む必要があります。 認証基盤を自前で実装することはほぼなく、クラウドサービス/SaaSを使うことになると思いますが、 前述の通り外部認証基盤とは別にスタンドアロンでのユーザ登録・権限操作をできる必要があり、 PoCシステムの認証基盤選定時には外部認証基盤連携とシステム内ユーザ管理の両方に対応しているかを考慮して検討することになります。 信頼性 安定して動作し、障害から迅速に回復できるかという観点です。 副特性として以下が含まれます。 副特性 説明 成熟性 テスト・もしくは使い込みにより、どの程度正常に動作すると言える状態になっているか 可用性 どの程度の時間、利用可能か 障害許容性 障害発生時にどの程度正常に動くか 回復性 中断もしくは故障時にユーザののぞむ状態に復元できる度合い PoCでは PoCでは作成したプロトタイプ自体に高い安定性が求められることは稀ですが、 検証中に入力したデータが障害によって失われてしまうと、そこまでの検証が無駄になってしまう可能性があるので、 データの回復性についてはある程度確保する必要があります。 言い換えると、PoCではRTO(目標復旧時間)に比べRPO(目標復旧時点)の優先度が高いということになります。 またPoCでは通常のシステムと使って操作の期間を指定できる場合もあり、保全すべきデータ断面が本番システムより限定的と言えます。 アーキテクチャへの影響 データ回復性の要件をアーキテクチャに反映しようとすると、 ユーザがPoCを操作する日時を指定できるのであれば適切なタイミングでバックアップを取る仕組みを検討することになります。 また指定できない場合は十分な頻度の自動バックアップをアーキテクチャに組み込む必要があります。 いずれの場合もクラウド上のDB/ストレージサービスであればある程度カバーされていることが多いので、 入力データの重要性・コストのバランスを考えて取得タイミング・バージョン数を追加設定することになるでしょう。 セキュリティ セキュリティ関連の品質の副特性としては以下が定義されています。 副特性 説明 機密性 アクセス権限に沿ったデータ開示ができているか インテグリティ システムが不正アクセス・改ざんを防止できるか 否認防止性 入力・操作が行われたことを後から証明できるか 責任追跡性 誰が行った操作であるか追跡できるか 真正性 人・メッセージ・サービス等が本物であることを証明できるか PoCでは PoCでは信頼できるユーザに限られた環境・期間で操作をしてもらうことが多く、その場合はセキュリティについては本番システム開発よりも要求レベルが低くなりますが、 個人情報や部外秘の情報を検証に用いる際には、機密性だけ本番相当の高いレベルを求められる可能性があります。 アーキテクチャへの影響 機密性を確保するためには、大まかに2点、PoC環境自体へのアクセス制御と、ユーザごとのアクセス範囲の管理がアーキテクチャ上の関心事となります。 PoC環境へのアクセス制御については前述のユーザ認証のほか、アクセス元IPアドレスによる制限などが考えられます。これは本番システム構築時に検討する内容と同様です。 PoC時点でユーザ権限別にデータ/機能を制限する必要がある場合は、PoC環境を複数作り同じ権限のユーザ群ごとに別環境を提供することも選択肢となります。 複雑な権限管理を実装せずに済むため、その分の労力を本当に検証したいことに充てることができます。 保守性 保守性は以下の副特性に分解されますが、それぞれPoCでのポイントが考えられるので、個別に検討していきたいと思います。 副特性 説明 再利用性 作ったシステム・構成要素が他の目的にも利用できるか 解析性 不具合の原因を特定しやすいか 変更性 コードを修正する際に不具合・機能低下のリスクを低く抑えられるか 試験性 システムのテストをしやすいか モジュール性 適切にモジュール化されているか 保守性:再利用性 作ったシステム・構成要素が他の目的にも利用できるか、という観点になります。 PoCでは PoCで開発するシステムの再利用性には2つの観点があります。 1つは、PoCで作った機能を本番向けシステムに持っていけるかという点です。 特に数理モデルを構築してのPoCなどは検証したロジックに変更を加えることなく本番システムに反映する必要があり、重要な品質特性となります。 もう1つは、他のPoCを実施する際に流用できるかという点です。 こちらは前者ほどシビアになる必要ありませんが、将来のPoC実施時のコスト低減・実装期間短縮に寄与します。 アーキテクチャへの影響 本番システムへの再利用性を確保するためには、ソフトウェア内のレイヤ分け・コードのディレクトリ分けなどを開発標準として整備し、再利用したいロジックがモジュールとして独立した状態にする必要があります。 Insight Edgeではデータサイエンティストとエンジニアの混成チームで取り組むことがありますが、 このような場合は、事前にメソッドの切り方や引数・戻り値まで定めておくことで、 お互いの作成するロジックに影響を与えることなくスムーズに分担できるようになります。 また、他のPoCで再利用できるようにするという観点では、PoCでよく使う機能・構造を部品やテンプレートとして用意しておくことが考えられます。 その際は、本記事でそれぞれの品質特性に対して検討した内容がテンプレートに取り込む要素の候補になるでしょう。 保守性:解析性 不具合の原因を特定しやすいか、という観点です。 PoCでは PoCでは予定していなかった操作・入力データに出会い、それを分析することが重要な要素です。 通常、使用状況の把握/不具合の調査に使える情報の量とログ保管容量/コストはトレードオフの関係になりますが、 PoCは本番システムに比べるとデータ量が少ないため、分析のトレーサビリティに振り切るという選択が可能です。 アーキテクチャへの影響 機能適合性についての検討でユーザ操作の記録をPoC環境の基盤機能として検討しましたが、不具合時の解析性ではリソース使用状況のメトリクスとログも重要な情報となります。 メトリクスについてはクラウドサービスを使っている場合はCPU/メモリ等のリソース使用状況を取得する設定ができると思いますので、PoCに合わせた設定値を検討して環境構築時に確実に適用できるよう管理しましょう。 ログについては効果的なログの取得タイミングが開発ガイドラインとして整備されていると良いでしょう。 またログのフォーマットとしては構造化ログを採用しましょう。主要なクラウドのログ管理サービスは構造化ログに対応していますので、適切にフィールドを使えば、多量のログの中からでも必要な情報を見つけやすくなります。 Insight Edgeではカスタムのログライブラリを作成し、ログの取り扱いの労力を減らしています。 保守性:修正性 コードを修正する際に不具合・機能低下のリスクを低く抑えられるかという観点です。 PoCでは PoCでは「フィードバックをもとに改修を加えて再度検証する」というサイクルを何回か実行することがあり、素早く安全に修正できることが求められるますが、本番システムも同様に素早く安全に修正できるべきなのでPoC開発だけの品質要件というわけではありません。 保守性:試験性 試験性はシステムのテストをしやすいかという観点です。 PoCでは PoCでは2つの面から試験性を捉えることができます。 1つは開発者が修正した内容を素早く確認でき、PoC環境へデプロイ可能な状態にもっていけるかという点です。 これはPoCに限らず本番システム開発でも求められる特性となります。 もう1つは、PoC自体が一種のテストなので 検証したい内容を正しく実施・検証できるようになっているかという点です。 検証に必要な機能が備わっているかという、機能適合性について検討することでカバーされます。 保守性:モジュール性 適切にモジュール化されているかという観点です。 PoCでは 適切なモジュール化はこれまで述べてきた保守性の全ての副特性に関わる要素です。 PoCごとにそれぞれの副特性の観点で検討することにより「適切なモジュール化」が決まります。 モジュール化を目的とするよりも、それによって達成したい特性からどのようなモジュール構造にするべきかを検討し、アーキテクチャへ反映する形となります。 性能効率性 システム側のリソースを効率的に使用できているかという観点です。 PoCでは PoC段階で検証の主役になることはあまりないでしょう。 移植性 移植性には下記が含まれます 副特性 説明 適応性 多様な環境をターゲットにしている場合に適用できるか 設置性 インストール・アンインストールのしやすさ 置換性 別の製品で置き換えられるか PoCでは PoCはそもそも本番に向けて細部・周辺を更新することを前提としているので、 完成品を他の環境に持っていけるかというでPoC開発時に検討する意味は薄いと考えられます。 品質特性に対するまとめ ここまでSQuaREで定義された製品品質ごとにPoC用システムで求められる要件・アーキテクチャについて考察してきました。 PoCという一時的な取り組みのためのシステム開発であっても、それぞれの製品品質の観点で検討することで、本番開発と異なる点を発見しアーキテクチャ設計時の考慮事項を言語化できたように思います。 コスト(Cost)・納期(Delivery)も考えると ここまで品質:Qualityの面で検討しましたが、PoCはプロジェクトとして実行されるので、当然コスト・納期も重要な要素となります。 上記の品質面で検討した内容と、コスト・納期のバランスを取るにはどのようなアーキテクチャ設計上の判断が可能でしょうか。 既存システムからの流用 1つには、チームメンバがよく知っているアーキテクチャパターンを採用、もしくはチームが管理している他のシステムから流用することが考えられます。 ここまで品質面から検討してきたアーキテクチャの要素と一致しない部分も出るためPoCとしての要件に対して最適ではない可能性もありますが、 短期間・検証目的というPoCの位置付けを考えると、品質の妥協によるデメリットよりもコスト・時間を省力できるメリットの方がインパクトが大きいと考えられます。 また品質に関しても、すでに理解が進んでいるインフラ・アプリ構成となることで 既存システムに関わるチームメンバにとっては 保守性が高い状態となりますので、トレードオフ検討時にはその点も考慮に入れる必要があります。 共通機能の整備 もう1つ、組織としてPoCを頻繁に行うならば、PoCに特化したアーキテクチャ部品を再利用可能な形で持っておくことが考えられます。 システム全体のアーキテクチャのうち、Dev(Sec)Opsにあたる部分、 CI/CDの仕組み 監視の仕組み モニタリングの仕組み などはPoCの内容が変わっても共通であることが多いです。 これらを意識的に再利用し適宜アップデートをして整えていくことで、 PoC時には検証したい機能・UI等の実装にリソースを集中できるようにすると良いでしょう。 まとめ 今回は各品質特性についての考察を中心にPoCならではの特性・アーキテクチャ設計時の考慮事項を検討してみました。 元々なんとなくプロトタイプの設計時に対応していたものも多いですが、 言語化したことで抜け漏れなく検討できるようになったように思います。 それぞれの企業、チーム、プロジェクトによって状況などは異なると思いますので、 アーキテクトの皆様の検討の枠組み・叩き台として参考になれば幸いです。
アバター
こんにちは、Insight Edgeでリードデータサイエンティストを務めている ヒメネス(Jiménez) です! 前回の投稿 から丸1年経ちましたが、改めて皆さんと知識共有できればと思います。今回は、話題のOpen Source LLM(Llama, Mistral, DeepSeek等)をローカルで実行する方法を紹介します。 目次 LM Studioの紹介 LLMをローカルで実行 準備 Pythonプログラムから実行 単体実行 OpenAIを通した実行 活用例:討論する哲学者 哲学者の定義 討論内容の定義 討論の実施 討論の要約 まとめ LM Studioの紹介 LM Studioは、ローカル環境で大規模言語モデル(LLM)を簡単に実行できるプラットフォームです。GUIベースで操作できるため、エンジニアだけでなく、非エンジニアでも手軽に試すことができます。 【特徴】 直感的なUI :モデルの管理やプロンプト実行を簡単に操作可能。 多様なモデル対応 :Llama、Mistral、DeepSeekなど、さまざまなモデルをサポート。 ローカル実行 :APIを通じてローカルで高速にモデルを実行可能。 LLMをローカルで実行 準備 まずは 公式サイト からLM Studioをダウンロードし、インストールしましょう。アプリを開くと、以下の画面が表示されます。下のメニューから、 Power User モードを選択します。すると、左側のメニューが現れます。 モデルをダウンロードするために、左メニューの4つ目の選択肢、「検索」をクリックします。このデモで利用するモデルは、 日本語にファイチューニングされた 「Llama-3-ELYZA-JP-8B-GGUF」と言います。検索バーで「elyza」を記入し、モデルを選択し、ダウンロードしましょう。 ⚠️ 補足 :「なんでも答えてくれる」モデルもありますが、利用目的に合ったモデルをダウンロードした方がより適切な回答を得られます。例えば、Metaが公開しているLlamaをそのまま使用しても日本語で答えてくれますが、質問の意図を理解できない、無関係の回答が返されることもあります。 モデルに期待できる回答の確認をするために、左メニューの1つ目の選択肢、「チャット」をクリックすると、そのままダウンロードしたばかりのモデルと対話できます。 ⚠️ 補足 :初回の実行だけ、モデルがダウンロードされていてもメモリ上でロードされるまで少し時間がかかります。 Pythonプログラムから実行 Pythonプログラムから利用するために、左メニューの2つ目の選択肢、「開発者」をクリックします。表れるメニューのURL情報を記録します。 以下のようにPythonで登録します。 local_url = 'http://127.0.0.1:1234' LM Studioでダウンロード済みのモデルの内部名称を確認するために以下の関数を使います。 import requests, json response = requests.get(f '{local_url}/v1/models' ) models = json.loads(response.text) print ([m[ 'id' ] for m in models[ 'data' ]]) 出力はこのようになります: [ 'llama-3-elyza-jp-8b' ] 利用するモデルと、投稿したいメッセージを定義します: model = 'llama-3-elyza-jp-8b' messages = [{ 'role' : 'system' , 'content' : 'あなたは便利なAIアシスタント。' }, # チャットボットの役割 { 'role' : 'user' , 'content' : 'こんにちは!' }] # ユーザーの質問・コメント # データをjson形式として保存 data = { 'model' : model, 'messages' : messages} 単体実行 では、いよいよチャットボットから回答を求めましょう! # 回答を求める response = requests.post(f '{local_url}/v1/chat/completions' , json=data) response_content = json.loads(response.text) print (response_content[ 'choices' ][ 0 ][ 'message' ][ 'content' ]) # 辞書として中身を取得 実行結果: こんにちは!話を聞くことが大好きなAIアシスタントです!どうぞよろしくお願いします。 OpenAIを通した実行 OpenAIのAPIを使って、モデルを呼び出すことも可能です。 from openai import OpenAI # クライアントを作成 client = OpenAI(base_url=f '{local_url}/v1' , api_key= 'lm-studio' ) # 回答を求める response_content = client.chat.completions.create(**data) print (response_content.choices[ 0 ].message.content) # オブジェクトとして中身を取得 実行結果: こんにちは!話しかけてくれてありがとうございます!私はあなたの便利なAIアシスタントです。何でも相談したり、質問してくださいね! ⚠️ 補足1 :1回目の実行は 単体実行 より実行時間が長いが、2回目以降は同じ速度で回答が返ってきます。 ⚠️ 補足2 :辞書を引数として展開する方法に慣れていない方は、 **data の代わりに以下を使用してください。 response_content = client.chat.completions.create( model=data[ 'model' ], messages=data[ 'messages' ] ) 活用例:討論する哲学者 ローカルで複数のLLMを走らせられるため、LLM同士の討論をシミュレートできます! 哲学者の定義 哲学者には以下の機能を求めます: 1. LMStudioのLLMが使える 2. プロンプトに回答できる 3. 会話の履歴を記憶できる class Thinker : """哲学者のクラス""" def __init__ (self, local_url, model): """初期化""" self.local_url = local_url self.model = model def set_system (self, name, system_prompt): """チャットボット(哲学者)の役割を設定、文脈共有""" self.name = name # チャットボットの名称 self.history = [{ 'role' : 'system' , 'content' : f 'あなたは{name}です。{system_prompt}' }] # 会話履歴を初期化 def reply (self, user_message): """ユーザーの質問に回答""" # 会話履歴にユーザーの質問を追加 self.history.append({ 'role' : 'user' , 'content' : user_message}) # 回答を求める response = requests.post(f '{self.local_url}/v1/chat/completions' , json={ 'model' : self.model, 'messages' : self.history}) response_content = json.loads(response.text)[ 'choices' ][ 0 ][ 'message' ][ 'content' ] # 会話履歴に回答を追加 self.history.append({ 'role' : 'assistant' , 'content' : response_content}) # ここでassistantは自分のことを指す(userの反対) # 回答を返す return response_content 討論内容の定義 テーマ、役割や回答形式など定義します。Insight Edgeはせっかく生成AIを活かす企業なので、今回の討論テーマは「 生成AIの普及によって、データサイエンティストの仕事がなくなると思いますか? 」にしたいと思います。哲学者はそれぞれ、「 仕事がなくなっていく 」派と、「 生成AIを有効に活かせる 」派のデータサイエンティストにします。 # 討論内容と哲学者の考えを定義 topic = { 'question' : '生成AIの普及によって、データサイエンティストの仕事がなくなると思いますか?' , 'thinkers' : [ '生成AIのせいで仕事がなくなると強く思うデータサイエンティスト' , '生成AIのおかげで仕事の効率が上がると強く思うデータサイエンティスト' ]} # 哲学者から求める細かい共通振る舞い common_instructions = """あなたは討論に参加しています。合理的でありながら、可能な限り自分の視点を擁護しなければなりません。あなたの言語スタイルは丁寧です。 あなたは非常に強い意見を持っており、何があっても自分の視点でユーザーを納得させる必要があります。 はい/いいえの質問に答えるとき、**必ず**「はい」または「いいえ」で始めてから、考えを説明してください。もし反対意見がある場合は、はっきりと示してください。回答は80文字以内に制限してください。""" 討論の実施 哲学者を定義し、合意に至るまでループに入れます。 # 哲学者2人を作成 thinker1 = Thinker(local_url, model) thinker2 = Thinker(local_url, model) # 哲学者の役割を設定 thinker1.set_system(topic[ 'thinkers' ][ 0 ], common_instructions) thinker2.set_system(topic[ 'thinkers' ][ 1 ], common_instructions) # 会話履歴を初期化 history = [topic[ 'question' ]] # 最初の意見 answer1 = thinker1.reply(history[- 1 ]) + 'あなたは賛成しますか?' history.append(answer1) thinker1_agrees = False # 最初の意見だけ、一人しか話していないので賛成とは言えない i = 1 print (f '({i})哲学者1の意見が出ました。' ) # 合意が得られるまで、交互に質問と回答を繰り返す while True : # thinker2の回答 answer2 = thinker2.reply(answer1) + 'あなたは賛成しますか?' history.append(answer2) thinker2_agrees = True if answer2[ 0 ] == 'は' else False # 賛成するかどうか。「はい」の1文字目で確認 i += 1 print (f '({i})哲学者2は、哲学者1の意見に対して{"同意" if thinker2_agrees else "反対"}しました。' ) if thinker1_agrees and thinker2_agrees: break # thinker1の回答 answer1 = thinker1.reply(answer2) + 'あなたは賛成しますか?' history.append(answer1) thinker1_agrees = True if answer1[ 0 ] == 'は' else False # 賛成するかどうか。「はい」の1文字目で確認 i += 1 print (f '({i})哲学者1は、哲学者2の意見に対して{"同意" if thinker1_agrees else "反対"}しました。' ) if thinker1_agrees and thinker2_agrees: break print ( '哲学者が合意しました。' ) 実行結果 LLMの細かいパラメーターを調整していないため、結果が実行する度に変わります。以下は1回の実行で得られた討論です。 (1)哲学者1の意見が出ました。 (2)哲学者2は、哲学者1の意見に対して同意しました。 (3)哲学者1は、哲学者2の意見に対して反対しました。 (4)哲学者2は、哲学者1の意見に対して同意しました。 (5)哲学者1は、哲学者2の意見に対して同意しました。 哲学者が合意しました。 この実行で、討論は5ステップで収束しました! 討論の要約 討論の履歴を全部読むのが場合によって大変かもしれませんので、内容を要約する新しい哲学者を定義します。 # 会話の内容を簡潔にまとめる考案者を作成 thinker3 = Thinker(local_url, model) thinker3.set_system( '討論の目撃者' , f """元の質問は「{topic['question']}」でした。 これが彼らの最終的な発言です: {history[-2:]}""" ) # 内容をまとめるために、最後の2つの発言だけ参照 conclusion = thinker3.reply( '最終的に双方は合意に至ったようです。彼らは何に合意しましたか?' ) print (conclusion) 内容のまとめはこちらです: 彼らが合意したのは、生成AIの普及によって、データサイエンティストの仕事がなくなるということではなく、その専門性や創造性を活かす機会が減少することは反対で、人間特有の判断や洞察を必要とする高度な分析や戦略立案などは、AIに置き換えられない分野であり、これらのスキルを身に着けることで、我々データサイエンティストの価値を高めることができるという点です。 最後に、履歴の中身を確認したい場合は、 history をそのままプリントすれば良いです。 for text in history: print (text) まとめ LM Studioは、ローカルで大規模言語モデルを手軽に試せる強力なツールです。ローカルだからこそ複数のエージェントを定義でき、対話型のシミュレーションにも応用できます。プライバシーを重視しつつ、LLMの可能性を広げるために、ぜひご活用ください! ¡Hasta pronto!
アバター
はじめに こんにちは、Insight Edgeで営業・コンサルタントを担当している楠です。 普段は主に住友商事グループの事業会社向けのDX案件の企画・推進に携わっております。 今回の記事では、私が参加させていただいた社会人向け研修プログラム「Technology Creatives Program(通称テックリ)」の内容や学びについてご紹介いたします。デザイン思考に関心のある方や、テックリへの参加を検討している方への参考になれば幸いです。 なお、テックリの受講期間は3月までなのですが、本記事の内容は執筆時点(2月中旬)のものとなる点、あらかじめご留意ください。 目次 はじめに Technology Creatives Program(テックリ)とは 参加動機・きっかけ プログラムの内容・学び まとめ Technology Creatives Program(テックリ)とは 本題に入る前にTechnology Creatives Program(通称:テックリ)の概要について触れておきたいと思います。 テックリは、東京科学大学・多摩美術大学・一橋大学という、日本を代表する三大学が共同で提供する社会人向けの教育プログラムです。Technology Creatives Programという名前の通り、テクノロジー、クリエイティブ、及びビジネスの観点を俯瞰・統合し、これまでになかった製品・サービス開発を通して新たな価値創造を実現する人材を育成することを目的としています。 テックリについて知りたい方は 公式HP をご覧ください。 参加動機・きっかけ 弊社はMissionとして「To “Re-design” the world with the power of technology; 技術の力で世界を“Re-design”する」を掲げており、主に住友商事グループ企業のDX化を主軸に、グローバルの多種多様な事業と向き合い、テクノロジーで課題を解決するサポートを行なっています(参考: 採用 - Insight Edge )。 私も上記Missionに基づき日々業務にあたる中で、複雑な課題を解決するためには粘り強さと柔軟な思考が不可欠であると感じており、テックリでアート思考やプロトタイピング手法を学ぶことで、日々の業務に活かせるヒントを得たいと考えました。 また、後述の通り、テックリでは他の様々な会社・職種の参加者の方とチームを組んで一緒にワークすることが多いため、異なるバックグラウンドや価値基準を持つ方々と協業して成果を上げる経験や交流の機会が得られると考えました。 加えて、テックリは今年度で3期目となっており、弊社のデザインストラテジストが過去に参加していたことも大きなきっかけの一つです(当該メンバの過去記事は こちら )。 プログラムの内容・学び テックリは大きく「導入モジュール」「パーパスモジュール」「探索モジュール」「旅立ちモジュール」の4つのモジュールで構成されています。プログラムの内容説明は 公式HP に記載がありますが、ここでは自分の経験に基づき、記載のない部分について、私個人の学びと併せて少し補足したいと思います。約半年強のプログラムでの実施内容や学びはとても多く、全てを書ききることは難しいので、少しでも気になった方は是非受講を検討してみてください。 なお、今後実施されるテックリの内容については、最新の情報をご確認ください。また、コンテンツの権利保護に配慮し内容面の詳細な記述は避けています。 導入モジュール 概要 多摩センター周辺の研修施設にて2日間の合宿形式で、講師の方々による講義やワークを実施。 ワークでは、自治体職員の方からお題をいただいた上で、多摩センターで活動されている方々(地域住民やボランティアの方など)へのインタビュー及びフィールドワークを実施し、課題解決につながるソリューションのアイデア発想及び提案を行った。 印象的だった点・学び テックリの採用するアプローチ ソリューション検討では、インタビューに基づいてエクストリームユーザーのインサイトを抽出した上で、スケッチや段ボールなどでの簡単なプロトタイピングを行うアプローチをとりました。また、作成したプロトタイプを用いて自らスキット(寸劇)を行い、具体的なソリューションイメージの検討やすり合わせを行った点も印象的でした。 プロトタイプ・スキットの重要性 プロトタイプやスキットは、ソリューションのイメージを端的に伝える手段としてだけではなく、ソリューションについて考えを詰め切れていない部分に気づくための手段としても有効であると感じました。チームメンバでスキットを実施する中で「○○の機能はどうなってるんだっけ?」といった会話が生まれることで、検討できていなかった詳細な項目に気づく場面が何度もありました。 パーパスモジュール 概要 テックリ講師陣によるオンライン講義を受講。 多摩美術大学にて、博報堂ブランド・イノベーションデザイン様及びARS ELECTRONICA FUTURE LABの小川秀明氏による講義及びワークショップを実施。 Cooking Slam(調理実習型ワークショップ)を実施。各チームにあらかじめ設定と食材が割り振られた状態でオリジナルの料理を作成した。 印象的だった点・学び 「写真を撮る際は影が映らないように」 オンライン講義の一つでスケッチのレクチャを受けた際のことです。受講者が事前提出したスケッチの写真に対して講師の先生からフィードバックをいただく場面で私の番になった際、一通りスケッチに関する指導をいただいた後、「写真を撮る際は影が映らないように」というコメントをいただきました。曰く、影に気を取られるとメインである絵の方に集中できなくなってしまうとのこと。また、デザイナーの世界では、ミーティング後にスケッチを共有することがあるそうですが、その際も影や余計なものの映り込みはないようにしているとのことでした。普段から意識されている方にとっては当たり前のことかもしれませんが、私はあまり気にしていない部分だったので、大きな気づきとなりました。別の場面にはなりますが、プロトタイプを作成する際ワークショップの際にも「早く簡単に作ることと雑に作ることは違う」というコメントをいただいており、印象に残っています。プロトタイプは実際の製品とは異なり精巧に作りすぎる必要はないものの、ユーザ検証など一定の目的を達成するために丁寧に作る必要があると感じています。 実際の写真。タイトルは「出る杭は打たれる。ただし、出過ぎた杭は打たれない。」 調理とプロジェクトの共通性 Cooking Slamでは、限られた食材の中でオリジナルの料理を作成し、設定に沿ったストーリーと併せて発表しました。時間や使える食材が限られている中、チームで協力しながら素早く手を動かして完成品を作る行為は、仕事におけるプロジェクトワークと似ていると感じましたし、後の探索モジュールにも通じる部分がありました(ちなみに、ワークの中では調理中に様々な制約が追加されるのですが、私のチームは途中から包丁の使用を禁止されました…!)。 探索モジュール 概要 他の参加者と4名程度のチームを組み、パートナー企業様からのお題に対してプロトタイプ付きのソリューション提案を実施(導入モジュールの長期間版)。プロジェクト期間は約4か月で、この間にユーザ候補者へのインタビューやプロトタイピングを何度も繰り返して実施。最終成果物についてはポスター展示やプレゼンを実施。 印象的だった点・学び 「問い」の重要性 お題は抽象度の高いものが多く、初めにインタビューを通じて自分たちが想定するユーザや解決したい課題およびHMWQ(テックリでは解決したい課題に対するアプローチ方針をHow Might We Questionと呼ばれる「問い」の形で表現していました)を定義する必要があったのですが、これが相当大変な作業でした。広すぎる・狭すぎる・インサイトを捉えきれていない・テックリのアプローチでの解決が困難(例:医療処置など)など、講師陣のフィードバックを受けながら、インタビュー結果のメモを眺めて何度も検討しました。この部分が詰め切れていないと、必要とされないソリューションや実現不可能なソリューションができてしまうので、やはり「問い」の設定が最も重要な工程であると感じました。 「手を動かすこと」の重要性 探索モジュールに限らずテックリ全体を通した最大の学びがこちらになるのですが、情報収集・アイデア発想・ソリューション検討いずれにおいても、頭だけではなく手を動かして素早くプロトタイプを作成することが重要であると感じました。今回、チーム全員が社会人メンバで通常業務と並行して探索モジュールのプロジェクトを進める必要がある中、チームでの話し合いの時間は、どうしても日程調整・タスク整理・役割分担といったプロジェクト管理的なトピックであったり、アイデアを並べて「どれが良いか」を考える議論に割かれがちでした。しかし、インタビューを実施して、テーマや自分たちのソリューションに関する生の声を収集したり、アイデアやユーザ体験の絵を描いたり、実際のモノを作ったりしなければ、自分たちのソリューションに自信を持つことができません。なぜなら「答え」はユーザが持っているからです。私たちのチームは初期にこの「ワナ」にはまり苦労しましたが、終盤ではインタビュー時にプロトタイプを見せるようになり、ユーザから返ってくるフィードバックの質や量が大幅に改善されました。 旅立ちモジュール ※3月実施予定につき割愛 まとめ 今回の記事では、私が参加させていただいた社会人向け研修プログラム「Technology Creatives Program(通称テックリ)」の内容や学びについてご紹介いたしました。 体系化された知識というよりは自分自身の経験を通した学びが多かったですが、何某か皆様の参考になりましたら幸いです。自分自身も、今後業務を進める際の糧としていきたいと思います。 プログラム内容が盛りだくさんだったこともあり、書ききれていないことが多数あるのですが、ご興味のある方は是非受講を検討してみてください!
アバター
こんにちは! Insight Edge分析チームの梶原(悠)です。 最近ひょんな経緯で量子計算用のQmodという言語のフィジビリ兼ゆる勉強会に顔を出しています。 Qmod言語 1 はclassiq社という量子ベンチャーが提供している 無償 有償ツール※で、簡便に量子アルゴリズムを実装できる高水準言語をうたっています。 私は量子計算について何も知らない素人ですが、基本的なpythonと線形代数の知識があれば使えるとのことで、量子畑の人たちにあれこれ教えていただきながら、すこし触ってみました。 量子計算に興味や前提知識はないが、技術動向はある程度把握しておきたいと考える技術者の読み手を想定して、言語仕様の一部やツールに触れてみた感想などを書きます。 ※ 2025.3.7 classiq社様より無償ツールの記述は誤りであるとのご連絡を頂いた為訂正しました。公式サイト中の"free for non-comercial purpose"の記載に基づき無償ツールと解釈していたのですが、最近有償化されたそうです。この誤りを含む複数のご指摘に感謝申し上げます。 目次 はじめに Qmod言語の仕様調査 回路生成の試行 感想 はじめに 量子計算とは 古典的なコンピュータにおけるCPUのレジスタは一刻にひとつの状態しか取れません。 これは重ね合わせ状態の崩れた物理系を利用して実装されているためです。 一方、重ね合わせ状態にある物理系でレジスタを実装すれば、指数的に多くの状態を並列して時間発展させることができます。 量子計算では、指数的に大きな並列処理を利用して、複雑な計算を高速に実行します。 ただし結果の観測などに強い制約があり、どんな問題でも指数的に速く解けるわけではありません。 実用化に向けた現状 IBM社のロードマップ 2 ではエラーの影響なく量子計算を実行できるデバイスの提供は2029年以降とされています。 classiq社のセミナー講師の方の話では、量子計算には実ビジネスで意味のある活用例はまだ存在しないようです。 何かのイベントの質疑応答で理研の研究者の方も「量子乱数は実用されているが、最適化などの文脈で意味のある活用例はまだない」と回答されてました。 これらの話を聞くと、量子技術を付加価値にして実社会に役立つものづくりをすることは現時点で難しいように思えます。 しかし、興味深いベンチャーも出てきています。 量子計算が実用化すると楕円曲線暗号を現実的な時間内に解けるようになるため、既存の暗号通貨が攻撃される懸念があります。 BlocQ 3 というベンチャー企業では、暗号生成部分のみ量子計算を利用したブロックチェーン技術を採用し、 量子技術の実用化が進んでも進まなくてもワークする量子readyな暗号通貨を開発しているようです。 量子に限らず、まだ早すぎる技術をどう売るか考える際に、〇〇readyな〜という切り口は学ぶ処がありそうに感じました。 量子回路モデル Qmodは量子回路という計算モデルの記述言語です。 量子回路を提案したD.Deutschは計算器を次のように説明しています。 4 直感的には, 計算器とは, 系の動的な発展で入力状態の集合から出力状態の集合へと遷移する任意の物理系です. 各状態には, なんらかの自然なラベルがつけられていて, マシンは指定された入力ラベルの状態で準備されます. そして, いくらかの動作を経たのち, 出力状態のラベルが観測されます. 古典計算では、入出力のラベルやレジスタの状態は01列で表されていました。 量子計算でもラベルは01列ですが、レジスタは複素ベクトルで表されます。 古典計算の時間発展はレジスタの状態をコピーや論理演算子などでくりかえし変換して記述されました。 量子計算はレジスタにゲートと呼ばれるユニタリ行列をくりかえし作用させて時間発展を記述します。 時間発展は可逆でなければならず、状態のコピーはできません。 レジスタに作用させるゲートを並べた系列を量子回路 5 といいます。 下記の表に量子回路による計算の構成要素をまとめます。 用語 説明 キュビット (qubit) 大きさが1の2次元複素空間のベクトル をキュビットという. 量子レジスタ 大きさが1のn次テンソル空間のベクトル を長さnの量子レジスタという. キュビットの状態 6 トレースが1の半正定値なエルミート行列を密度行列という. キュビット で定まる密度行列 をキュビットの状態という. 量子レジスタの状態 量子レジスタ で定まる密度行列 を量子レジスタの状態という. 量子ゲート 量子レジスタ上のユニタリ行列を量子ゲートという. 量子回路 長さ のレジスタ上の量子ゲート は自然に長さ のレジスタ上のユニタリ行列 に拡張できる. 量子ゲート を長さ のレジスタ上に拡張したユニタリ行列の列 を𝑛ビット量子回路とする. 観測可能量 冪等 (すなわち ) な行列 を、部分空間 への射影作用素という. エルミート行列 を 番キュビットのオブザーバブルという. オブザーバブル の固有値 に対応する固有空間へのエルミートな射影作用素を とかく. 測定 量子レジスタ の キュビットの状態の測定とは以下の1. 及び2. を行うことである. 1. (統計公式) ビット列のサンプリング を行う. ただしビット列 に対応する射影作用素を とした. 2. (射影仮説) レジスタの状態を密度行列 から に変える. 量子回路は下図のような直感的なダイアグラムで記述することができます。 横線はキュビットを表し、四角い箱はゲートを表します。 横線の左端がキュビットの初期状態に対応し、左から順にゲートを作用させていくことで時間発展を記述します。 Qmod言語の仕様調査 コンパイル階層 量子計算を実行する物理デバイスによってキュビットの結合状況や利用できる基本ゲートの種類は異なります。 qmodツールは抽象的な量子回路を物理デバイス上で実行可能な量子回路へとコンパイルします。 qmodのコンパイル階層 7 の詳細は不明ですが、概ね上図のような流れと想像されます。 QNum型 qmodにはいくつかのQuantum型が定義されています。 QBit型はキュビットに対応します。 QNum型は量子レジスタに実数の固定少数点表現として解釈するためのメタ情報を付与したものと思われます。 いくつかのパタンを実験した限りでは基底ベクトルと実数は下記のように対応するようです。 量子レジスタの解放 qmodは量子レジスタの領域の割当や解放を自動的に行います。 Quantum型の変数に割り当てられた領域を明示的に解放させる方法として少なくとも以下の3通りはあるようです. bind(src,dst)関数のsrc側の引数にする. qfuncデコレータのついた関数のinput修飾子のついた引数にする. within_apply(within,apply)関数のwithin部で宣言、初期化する。 ライブラリ関数の例: 標準ゲート関数(X, Y, Z) X,Y,Z関数はそれぞれ以下で定義されるパウリ 演算子を表します。 # 分類・名称 記号 定義 基底 についての行列表示 1 パウリX演算子, NOT 2 パウリY演算子 3 パウリZ演算子 ライブラリ関数の例: linear_pauli_rotations関数 linear_pauli_rotations関数は以下の形で呼び出します。 (ただし、 はパウリ行列を指定するenum。) この関数は以下のようにレジスタ を変化させます. Input Output (ただし は引数basesで指定したパウリ行列.) linear_pauli_rotations関数はキュビットを行列 で変換する関数だとわかりました。 そこで行列 による変換が何を意味するかを考えてみます。 写像 を で定め、ホップ写像と呼びます。 ホップ写像を の単位球に制限すると の単位球への全射を与えます。 この全射の像をブロッホ球と呼びます。 キュビット を行列 で変換すると、ブロッホ球上の点 は点 へ動きます。 ホップ写像の定義式にパウリ行列 の指数を三角関数で表した を代入すると次の関係が導かれます。 したがって、行列 はブロッホ球を 軸周りに 回転させます。 また、 をそれぞれ にぐるりと入れ替えて同様の議論を繰り返せば、 行列 が 軸周りの 回転を引き起こすことがわかります。 同様にして、行列 は 軸周りの 回転を引き起こすことがわかります。 回路生成の試行 量子モンテカルロという手法で積分 の値を推定する回路を題材に、回路生成を試行してみます。 量子モンテカルロでは、積分値の推定問題をユニタリ行列の固有値問題に帰着して解きます。 大まかなアイディアとしては、まず推定したい積分値の情報を固有値として持つユニタリ行列を構成します。 次に位相キックバックというトリックを用いて固有値をキュビットの位相として取り出します。 最後に、位相を量子フーリエ変換の逆変換等で振幅に反映させて観測すれば、興味のある積分値を高速に推定できるという流れです。 推定したい積分値の情報を固有値として持つユニタリ行列を構成するには2つの材料が必要です。 ひとつめは、求めたい積分値に応じた角度 だけ初期ベクトルを回転させながらある実2次元部分空間 に送り込む作用素(state_loading)です。 ふたつめは、部分空間 を第一軸について鏡映する作用素(good_state_oracle)です。 これらの材料を掛け合わせることでGroverOperatorという作用素を構成できます。 GroverOperatorは空間 を 回転させるため、固有値が積分値の情報を持つ作用素を作れたことになります。 実装上は、材料となる2つの作用素(state_loading及びgood_state_oracle)を qmodの組み込み関数amplitude_estimationに与えるだけで、 qpeによる量子モンテカルロの回路を作成できます。 以下は上記の積分値を推定する回路を生成してtranspile前後の回路をOpenQASMという形式で取得する実装例です。 from classiq import * # バージョン 0.67.0 で試行. n_qpe = 5 D = 2 n_prn = 5 a = 2.0 * (math.pi / 6.0 ) / ( 2 ** n_prn) b = 0.0 def good_state_oracle (state: QNum[QBit]): x = QNum( "x_" , n_prn*D) q = QBit( "q_" ) bind(state, [q, x]) Z(q) bind([q, x], state) return state def state_loading (state: QNum[QBit]): x1 = QNum( "x1" , n_prn) x2 = QNum( "x2" , n_prn) q = QBit( "q" ) bind(state, [q, x1, x2]) hadamard_transform(x1) linear_pauli_rotations( bases=[Pauli.Y.value], slopes=[a], offsets=[b], x=x1, q=q # sin((ax+b)/2) ) hadamard_transform(x2) linear_pauli_rotations( bases=[Pauli.Y.value], slopes=[a], offsets=[b], x=x2, q=q # sin((ax+b)/2) ) bind([q, x1, x2], state) return state @ qfunc def main (phase: Output[QNum]): state = QNum[QBit]( "state" ) allocate(n_prn*D+ 1 , state) allocate_num(n_qpe, False , n_qpe, phase) amplitude_estimation( oracle=good_state_oracle, # S_psi space_transform=state_loading, # A phase=phase, packed_vars=state ) model = create_model(main) model = set_constraints( model, Constraints( max_width= 25 , optimization_parameter=OptimizationParameter.DEPTH )) model = set_preferences(model, Preferences( optimization_timeout_seconds = 3600 , timeout_seconds = 3600 * 2 , qasm3= True , custom_hardware_settings = CustomHardwareSettings( basis_gates=[ "rz" , "cx" , "sx" , "x" ], ), transpilation_option= "intensive" , optimization_level = 3 , debug_mode= False )) qprog = synthesize(model) circuit = QuantumProgram.from_qprog(qprog) qasm_transpiled = circuit.transpiled_circuit.qasm qasm_raw = str (circuit.qasm) 上記の回路生成を回路幅の制約や最適化有無を切り替えながら何度か実行してみました。 synthesize関数が生成したtranspile前後の回路の幅(キュビットの個数)と深さ(時間発展のステップ数)の関係のグラフを示します。 最適化なしの設定ではすべての回路が同じ幅になりました。最適化ありの設定で生成したtranspile後の回路には幅と深さのトレードオフが見られます。 一方、transpile前の回路で幅の制約を緩めていくと、深さはそのままで幅だけが広がっていくことが観察されました。 これは敢えて幅を広げた冗長な回路にすることで、transpile後の回路がより圧縮されるように準備しているものと想像します。 transpile後の回路の深さは幅23ほどで改善が頭打ちになり、その後はゲート数だけが悪化しています。 仕様上、幅と深さは同時に最適化できないため、最適な回路を生成するためには、ユーザ側で回路幅の適切な制約を探索する必要がありそうです。 感想 今回は量子計算用のqmod言語の仕様を調査しました。 個人的にはwithin_apply関数の仕様が特に興味深いと感じました。 回路最適化やシミュレーションの実行はサーバ側で走りますが、同時に複数プロセスから呼ぶとエラーになるようでした。 これはバリエーション検証をする上で不便だと感じました。 差別化の観点では、生成された回路サイズがqiskitより非常に小さくなることは印象的だと感じました。 qmodのベンダが保有する特許情報 8 を見ると、回路最適化の他にも誤り訂正やデバッグに関するものがあるようです。 これらの要素が今後どのようにツールの機能に反映されていくのか興味を覚えました。 参照 [1] Qmod in classiq python sdk https://docs.classiq.io/latest/classiq_101/registration_installations/#python-sdk-installation [2] IBM Quantum roadmap https://www.ibm.com/quantum/blog/ibm-quantum-roadmap-2025 [3] BlocQ, lnc. https://www.blocqinc.com/blocq-selected-for-prestigious-ict-startup-league/ [4] Deutsch, D. (1985). Quantum theory, the Church–Turing principle and the universal quantum computer. Proceedings of the Royal Society of London. A. Mathematical and Physical Sciences, 400(1818), 97-117. [5] 小澤正直, & 西村治道. (1998). 量子コンピュータの計算量 (応用函数解析の研究). 数理解析研究所講究録, 1039, 64-80. [6] 小澤正直. (2012). 量子測定理論入門 (講義, 第 56 回物性若手夏の学校 (2011 年度) 研究と人生の指針-Beyond the CoMPaSS of your field.-, 講義ノート). 物性研究, 97(5), 1031-1057. [7] Sahu, H., & Gupta, H. P. (2023). Quantum Computing Toolkit From Nuts and Bolts to Sack of Tools. arXiv preprint arXiv:2302.08884. [8] Justia Patents https://patents.justia.com/assignee/classiq-technologies-ltd
アバター