株式会社Insight Edgeのブログ - TECH PLAY

TECH PLAY

株式会社Insight Edge

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

201

 この記事は Insight Edge Advent Calendar 2025 の2日目の記事です!🦌🦌🦌🦌🛷🎅 1日目のニャットさんの記事 で紹介された、テックブログレビューエージェントのサポートのもと、なんとか間に合いました。会社でのアドベントカレンダーは初の試みですが、お祭り感があって楽しいですね。 はじめに  はじめまして。Insight Edgeデータサイエンティストのnakanoです。  LLMアプリケーションの開発において、「とりあえず動くもの」を作ることは比較的容易です。しかし、実用的なレベルにまで仕上げることは難しい課題です。その理由は、LLMアプリの良し悪しを測る評価軸が曖昧なままだと、改善の方向性が定まらず開発が迷走してしまうからです。そこで今回は、この課題を解決するアプローチとして、評価駆動開発による進め方をご紹介します。  本記事では、「書き込みや線引きがある紙面画像から、情報を抽出するアプリ」を題材に、評価駆動によるLLMアプリケーションの開発プロセスを解説します。 目次 はじめに 目次 プロジェクトの説明 評価駆動開発でLLMアプリケーションを育てる 評価駆動開発とは バージョン1の開発 1-1. 評価基準の設計 1-2. 正解データの準備 1-3. LLMアプリの開発 1-4. 評価 バージョン2の開発 2-1. 評価基準の修正 2-2. 正解データの修正と追加 2-3. アプリの改善 2-4. 評価 バージョン3, 4, 5, 6:ひたすらサイクルを回す まとめ プロジェクトの説明  今回開発するLLMアプリケーションは、 書き込みや線引きがある紙面画像から、情報を抽出する ためのツールです。 背景・課題  私は最近、GoogleのAIツール「NotebookLM」の音声解説機能にハマっています。NotebookLMはドキュメントをアップロードすると、AIが要約や音声解説を生成してくれるサービスです。昔読んだ論文や備忘メモをアップロードするだけで、音声解説を作れるため内容を楽しく振り返ることができます。しかし紙の本は、デジタル化する必要があります。また、私は本を読む際に、気になる箇所に線を引いたり、余白にメモを書き込んだりする癖があります。これらの個人的な情報も一緒にNotebookLMに取り込みたいと考えているため、標準的なOCRツールでは対応できません。 作るもの 本プロジェクト概要  そこで次の要望を満たす、紙面情報抽出アプリを作成します。 本文の抽出 : 撮影した紙面の画像から、本文テキストを抽出できること。 本文以外の情報の抽出 : 紙面の図や表の内容を音声解説に活用できるレベルでテキスト化できること。 書き込みや線引き情報の抽出(重要) : 読書時に本に書き込んだメモ書きや線引き情報を抽出し、読者がその時気になったポイントをテキスト化できること。  これらの要望を満たすアプリを作ることで、過去に読んだ紙の情報をポッドキャスト化し、楽しく振り返ることができるようになります。 評価駆動開発でLLMアプリケーションを育てる 評価駆動開発とは  評価駆動開発とは、最初に評価基準を設計し、その基準に基づいてアプリケーションを反復的に改善していく手法です。 ただし、プロジェクトの序盤では評価基準自体がわからなかったり、データを見ていくたびに新しい評価基準が見つかったりします。 そのため本記事では、評価精度の改善以外に、評価基準の修正も含めて反復的に開発を進めていきます。 評価駆動な開発サイクル  このフローは、 Eval Driven System Design - From Prototype to Production の記事を参考に、自分のプロジェクトに合わせてアレンジしたものです。過去にこちら( データサイエンティストが評価駆動手法を使ってみた〜家計簿分類プロジェクトの実践記〜 - Insight Edge Tech Blog )のテックブログで触れているため、よければご参照ください。 バージョン1の開発  評価まで含めた最初のサイクルを素早く回すために、バージョン1では本文テキストとページ番号を抽出する程度のシンプルなLLMアプリケーションを作成します。 1-1. 評価基準の設計  最初に評価基準を設計します。抽出した情報(ページ番号,本文テキスト)の正確性を評価するための指標です。 ページ番号 (page_number)は、完全一致で評価します。 理由はページ番号は1文字でも間違うと意味が変わってしまうためです。 本文テキスト (context)は、レーベンシュタイン距離を正規化したものを採用します。 理由は、シンプルなため解釈性が高く、部分的な誤りを評価できるためです。 Pythonでの実装例は以下の通りです。 # 抽出アプリの出力フォーマット class ExtractedPageContentV1 (BaseModel): context: str = Field(..., description= "書面の画像から抽出された本文" ) page_number: str = Field(..., description= "ページ番号" ) # 正解データのフォーマット(たまたま、出力フォーマットと同じ) class GroundTruthV1 (BaseModel): context: str = Field(..., description= "書面の画像から抽出された本文" ) page_number: str = Field(..., description= "ページ番号" ) # 本文テキストの正確性を評価する関数 import Levenshtein def calculate_normalized_distance (pred_context: str , true_context: str ) -> float : distance = Levenshtein.distance(pred_context, true_context) normalized_distance = 1 - distance / max ( len (pred_context), len (true_context)) return normalized_distance 1-2. 正解データの準備  次に正解データを作ります。最初は5~20件程度で良いと考えます。理由は、プロジェクトを進めていく中で要件やスコープが変わることも多く、最初から大量の正解データを作成するのは非効率だからです。そのためまずは全体を代表するような5件のデータをしっかり作成します。 入力画像の例(中央公論新社『中国農村の現在』(山田昌弘著)より) 実際にPythonで表現すると以下のようになります。 # 正解データ(手作業で作る) ground_truth_dataset_v1 = [ { "input_path" : "./data/中国農村の現在/中国農村の現在 - 70.jpg" , "ground_truth" : { "page_number" : "56" , "context" : "とぎ汁などを混ぜてグツグツ煮て作るので、そのコストはゼロである。 \n " " 養豚は自家消費のためでもある。 \n " # 中略(実際には全12行のテキストを記載) "えあるという。そのように生産性の低い農地経営の中で、養豚こそが彼の主な収益源とな \n " "っているのである。トウモロコシは養豚の飼料となり、もし余れば販売することも可能。そ" , } }, # ... (他の4件も同様に作成) ] 1-3. LLMアプリの開発  次に処理を実装します。 最初のサイクルなので、シンプルですぐに実装できるアプリケーションを作成します。 プロンプトも特に工夫はせず簡単に記述します。 def extract_page_content_v1 (image_path: str ) -> ExtractedPageContentV1: with open (image_path, "rb" ) as f: image_bytes = f.read() response = client.models.generate_content( model= "gemini-2.5-flash" , config={ "response_mime_type" : "application/json" , "response_json_schema" : ExtractedPageContentV1.model_json_schema(), }, contents=[ types.Part.from_bytes( data=image_bytes, mime_type= "image/jpeg" , ), "あなたは、書面の画像から記述内容を抽出する専門家です。" "page_numberには、このページのページ番号を整数で入れてください。" "もし、ページ番号がわからない場合は空欄にしてください。" "contextには、記載されている日本語をすべて抽出してください。" "ただし抽出するテキストは本文だけで、ヘッダーやフッター、ページ番号などは含めないでください。" "改行がある位置には改行コードを入れてください。" "段落の最初の空欄には全角スペースを入れてください。" ], ) response_json = response.parsed result = ExtractedPageContentV1.model_validate(response_json) return result  実際に抽出された情報は以下のような感じです。 パッと見は期待通り文字情報を抽出できています。 ExtractedPageContentV1( page_number= '1' , context= 'まえがき \n 今世紀に入って、中国は世界最大 <略> りの農民国家で、正式な統計は' ) 1-4. 評価  実際に正解データを利用して評価を行います。結果は以下のとおりです。 No. ページ番号一致 本文スコア(正規化レーベンシュタイン距離) 1 True 89.9% 2 True 81.4% 3 True 35.5% 4 True 96.9% 5 True 95.1% ページ番号は全件正解、本文テキストも平均80%近くの精度が出ています。 一方で、3件目のファイルの精度が極端に低いことがわかります。 各データを確認したところ以下のような課題が見つかりました。 課題1 : 図表を誤って本文として抽出してしまっている 3件目のファイルの精度が35%と極端に低い要因は、図に記載されたテキストを本文として抽出していたためでした。 バージョン1では、本文テキストだけを対象としているため、図表の情報は含めないことが期待する動作でした。 バージョン2での対応として、プロンプトに「図表の情報に含まれるテキストは含めないでください。」を追加することにします。 課題2 : 改行コードの入れ忘れ 全ファイル共通して、改行コード \n の入れ忘れのために精度が落ちていることもわかりました。 改行コードは、1ページの中に10数個あるため、全体的な精度への影響は無視できません。 改善施策としては、プロンプトを修正して改行コードの入れ忘れを防止することも考えられます。 しかし、改行コードの有無は最終的な用途への悪影響はありません。 これは 評価指標と実務の嬉しさが乖離している ため、次のステップでの対応としては、評価時に改行コードの有無を無視するように評価基準を調整します。 課題3 : 最初/最後の行は、撮影時に影が映り込みやすく誤認識が多い。 紙面を撮影している都合上、最初と最後の行は、撮影時に影が映り込みやすく、誤認識が多いことがわかりました。 逆に最初と最後の行が正確に認識できている場合、中間の行も比較的正確に認識できていると考えられます。 その他の課題 : その他の細かい誤認識もいくつか見つかった 「て」を「で」と変換するなど、単純なOCRミス。 「もし余れば販売することも可能。」を「もし余れば市場に出すことも可能。」と、意味を変えるような誤認識。 段落の開始を示す、全角スペースの入れ忘れ。 性能以外課題 : 性能以外の課題として以下のようなものもある。 正解データの作成に時間がかかりすぎる。 グラフや地図の情報を抽出できない。 書き込みや、線引き情報を抽出できない。 バージョン2の開発 バージョン1の課題を踏まえて、バージョン2では以下のように改善を行います。 評価時に、改行コードの有無は無視する。改行コードを正確に抽出できるかどうかは、最終的な用途にとって重要ではないため。 本文テキストの精度は、最初と最後の行だけで評価する。撮影の都合上、最初と最後の行は陰になりやすくOCRの難易度が高いため。 図表の有無も判定する。図表の要約も行わせるが、評価はしない。理由は、工数が増えるため。 線引きや書き込み情報の抽出精度を評価する。読者が気になったポイントを抽出することが本アプリケーションの重要な目的であるため。 その他の細かい発生したミスを改善するようにプロンプトを修正する。例えば、OCRミスや段落開始の全角スペース入れ忘れなど。 2-1. 評価基準の修正  評価基準は以下のように修正します。 正解データ作成の工数を削減しつつ、実務の嬉しさと一致するように微調整しています。 本文テキスト (context) 最初の行と最後の行についてそれぞれのレーベンシュタイン距離を正規化したものを採用します。 正解データから最初の行と最後の行の文字数を抽出し、その部分だけを評価対象とします。 評価時に改行コードの有無は無視します。 図表の有無 (has_figures) 真偽値での完全一致評価を行います。 線引きテキスト (highlighted_texts) 抽出したテキストの と で囲まれた部分をリストとして抽出します。 正解データと部分一致でマッチングを行い、マッチング率を算出します。 書き込みテキスト (annotation_texts) 抽出した書き込みテキストのリストを正解データと部分一致でマッチングを行い、マッチング率を算出します 2-2. 正解データの修正と追加  評価基準の修正に伴い、正解データのフォーマットも修正します。 # 出力フォーマットの修正 # - 本文の中で線引きされている領域は<highlighted></highlighted>で囲むように指示する class ExtractedPageContentV2 (BaseModel): page_number: str = Field(..., description= "ページ番号" ) context: str = Field(..., description= "書面の画像から抽出された本文" ) has_figures: bool = Field(..., description= "図表の有無" ) figures_summary: str = Field(..., description= "図表の要約" ) annotation_texts: List[ str ] = Field(..., description= "書き込みテキストのリスト" ) #正解データのフォーマットの修正 class GroundTruthV2 (BaseModel): page_number: str = Field(..., description= "ページ番号" ) context_first_line: str = Field(..., description= "本文の最初の行" ) context_last_line: str = Field(..., description= "本文の最後の行" ) has_figures: bool = Field(..., description= "図表の有無" ) annotation_texts: List[ str ] = Field(..., description= "書き込みテキストのリスト" ) highlighted_texts: List[ str ] = Field(..., description= "線引きテキストのリスト" ) ground_truth_dataset_v2 = [ { "input_path" : "./data/中国農村の現在/中国農村の現在 - 70.jpg" , "ground_truth" : { "page_number" : "56" , "context_first_line" : "とぎ汁などを混ぜてグツグツ煮て作るので、そのコストはゼロである。" , "context_last_line" : "っているのである。トウモロコシは養豚の飼料となり、もし余れば販売することも可能。そ" , "has_figures" : False , "annotation_texts" : [], "highlighted_texts" : [ "のちにようやく料理の塩辛さの一要因がわかった。" , "「負担」とは、就学年齢の子供がいて現金収入が必要な事を指し" , ], }, }, # ... (他の9件も同様に追加) ] 2-3. アプリの改善  改善方針を受けて、LLMアプリケーションを以下のように修正します。 出力形式とプロンプトのみ修正しています。 def extract_page_content_v2 (image_path: str ) -> ExtractedPageContentV2: with open (image_path, "rb" ) as f: image_bytes = f.read() response = client.models.generate_content( model= "gemini-2.5-flash" , config={ "response_mime_type" : "application/json" , "response_json_schema" : ExtractedPageContentV2.model_json_schema(), }, contents=[ types.Part.from_bytes( data=image_bytes, mime_type= "image/jpeg" , ), "あなたは、書面の画像から記述内容を抽出する専門家です。" "page_numberには、このページのページ番号を整数で入れてください。" "もし、ページ番号がわからない場合は空欄にしてください。 \n\n " "contextには、記載されている日本語をすべて抽出してください。" "ただし抽出するテキストは本文だけで、ヘッダーやフッター、ページ番号などは含めないでください。" "改行がある位置には改行コードを入れてください。" "段落の最初の空欄には全角スペースを入れてください。" "蛍光ペンや赤ペンで線引きされている部分は<highlighted>と</highlighted>で囲んでください。" "抽出するテキストは本文のみであり、図や表の情報に含まれるテキストは含めないでください。 \n\n " "has_figuresには、このページに図表が含まれている場合はTrue、含まれていない場合はFalseを入れてください。 \n\n " "figures_summaryには、図表がある場合、その内容を簡潔に要約して記述してください。 \n\n " "annotation_textsには、このページに手書きの書き込みがある場合、その内容をすべてリスト形式で入れてください。" "もし書き込みがない場合は空のリストを入れてください。 \n\n " ], ) response_json = response.parsed result = ExtractedPageContentV2.model_validate(response_json) return result 出力結果は以下のようになりました。今回もパッと見は期待通りの情報が抽出できていますが、実際の精度はどうでしょうか? ExtractedPageContentV2( page_number= '56' , context=( 'とぎ汁などを混ぜてグツグツ煮て作るのとで、...途中略...' 'もし余れば<highlighted>販売</highlighted>することも可能。' ), has_figures= False , figures_summary= '' , annotation_texts=[], ) 2-4. 評価  精度評価した結果は以下の通りです。 本文テキストの性能はやや改善されているように感じます。しかし、そろそろデータ数を増やして行かないと本当に適切なのか、わからなくなってきました。 また、線引きテキストや書き込みテキストの抽出性能も計測できています。 No. ページ番号一致 図有無の判定 本文開始行スコア 本文最終行スコア 線引きテキストスコア 書き込みテキストスコア 1 True True 93.75% 92.68% 13.46% 100.00% 2 True True 100.00% 97.56% 100.00% 100.00% 3 True True 100.00% 94.12% 0.00% 100.00% 4 True True 100.00% 97.37% 6.90% 0.00% 5 True True 100.00% 90.24% 87.50% 100.00% 6 True True 90.24% 100.00% 100.00% 68.75%  本文抽出に関しても90%程度は達していますが、20文字に1文字は誤認識があると考えるとまだまだ改善の余地があります。 またこの評価用データはひとつの本から抽出したものなので、他の本に対しても同様の精度が出るかは不明です。 次のバージョンからは、データを増やし課題に漏れがないかなど再度見直しながら改善を進めていきます。  線引きテキストや書き込みテキストスコアの精度はまだまだ改善の余地があります。 特に書き込みテキストスコアは、書き込みが無いページを書き込みなしと判断すれば100%となるため、現在の指標は不適切な気がします。 バージョン3, 4, 5, 6:ひたすらサイクルを回す バージョン3以降は、次の表のとおり細かく修正していきました。線引きや書き込みの精度は割愛し、本文抽出についてだけ紹介します。 バージョン 方針と結果 3 方針 ・正解データを6件→50件に増やす。 ・新書以外にも技術書、横書きの本など多様な形態を追加する。 結果 ・データ種類を増やしたg、平均精度は概ね良好 ・一部大外しするケースが発生(原因:長い脚注など特殊なレイアウト) 4 方針 ・長い脚注を無視するようプロンプトに追記する。 ・具体的には「各頁にある脚注など本文外のテキストは含めてはならない」と記載。 結果 ・大外しするケースは激減した。 ・数字や記号の全角半角の表記ゆれなど細かいミスが目立つ。 5 方針 ・プロンプトが長くなってきたため、Geminiのベストプラクティスに従い全体的に修正 ・参考: https://ai.google.dev/gemini-api/docs/prompting-strategies 結果 ・ 段落開始の全角スペース入れの遵守率が大幅に改善 6 方針 ・数字や記号の全角半角の表記ゆれが起きないよう細かくプロンプトに指示する。 結果 ・ 記号数字での減点が減少し、さらに精度が向上 これらの修正によって、下記のグラフの通り精度を着実に改善できました。 各バージョンの抽出精度推移  一方で依然として生成AI特有の課題は残っています。この点はGeminiではなく、専用のOCRモデルを組み合わせるなどの対策が必要と考えられます。 読みにくい箇所を想像する(前に述べたようにを前述したように)など 有名人と1文字違いの人名を誤認識する まとめ  本記事では、「書き込みや線引きがある紙面画像から、情報を抽出するアプリ」を題材に、評価駆動開発でLLMアプリを実用化レベルまで育てるプロセスを解説しました。 評価駆動開発の3つのメリット 改善の方向性が明確になる LLMアプリはなんとなく良さそうと確認しながら進めがちですが、明確に方向性を決めることができます。 反復的な改善が可能 LLMアプリ開発ではプロンプトを細かく調整していきますが、改善を確認しながら着実に改善できるようになります。 ステークホルダーとの合意形成がしやすい 定量的な指標により、開発進捗や品質を客観的に共有できるようになります。 実践での学び 正解データの作成コストは侮れない。最初は評価データを絞った方が良い 評価指標は、最終的な用途と乖離しないよう注意深く設計する 少数のデータで素早くサイクルを回し、課題を発見してから本格的にデータを増やす おわりに  LLMアプリケーションを「とりあえず動く」状態から「実用レベル」に育てるプロセスを紹介しました。 本記事で紹介したプロセスを参考に、皆さんもLLMアプリケーションを着実に実用レベルまで育ててみてください。  明日のInsight Edge Advent Calendar 3日目は、因果推論とLLMに関する記事です!お楽しみに🎄🎄🎄🎄🎄
はじめに こんにちは、アジャイル開発チーム兼Insight Edge Techblog編集チーム担当のニャットです。 以前、 Vertex AI Geminiを使った社内議事録生成アプリ の記事で生成AI案件への挑戦について書きましたが、その後、生成AI案件にも少しずつ慣れてきました。とはいえ、生成AIの進化があまりにも速すぎて、キャッチアップの日々が続いています。笑 最近は、Claude Codeのコマンド、サブエージェント、スキルといった新しくリリースされた機能をいじってみることを楽しんでいます。そしてClaude Code Actionを使ってGitHub上でこれらの機能を活用できるように仕組み構築も色々試行錯誤しています。 その中の1つとして、テックブログレビューエージェントをマルチエージェント構成で構築したので、今回はその取り組みについて紹介します。 今回のレビューエージェントの構成はテックブログレビューにとどまらず、他のドキュメントレビューやコードレビューにも応用できると思いますので、ぜひ参考にしていただけると嬉しいです! なお、弊社では以前LangGraphベースでTechblogレビューエージェントを開発し紹介していましたが、最近はClaude Code Actionベースに置き換えていますので、その変遷についても説明します。 はじめに 本記事について LangGraphからClaude Code Actionへ - Techblogレビューエージェントをみんなで進化させてみた1年間 1. LangGraphでのエージェント作成からスタート(やってみる) 2. Claude Code Actionも併用して活用してみる(みんなでやる) 継続運用で見えてきた課題と改善ポイント 3. マルチエージェント構成への進化させてレビュー質を高める(やり抜く) 1. 執筆フローに合わせた段階的レビュー 2. レビューの質を高める仕組み 3. レビューの効率化 現在のTechblogレビューエージェント詳細 レビューエージェント構成 記事レビュー例 /outline-review の実行結果 /initial-review の実行結果 工夫点 カスタムスラッシュコマンドのワークフロー制御でレビューフローの最適化 サブエージェント分離によるコンテキスト節約とレビューの深さ向上 サブエージェントからメインスレッドへの結果受け渡し:JSON設計の工夫 GitHub Suggestion機能を確実に動かすための指示の工夫 MCPツールの積極的な活用 Web検索・取得機能の有効化 残っている課題と今後の改善点 まとめ Insight Edge Advent Calendar 2025が始まりました! 本記事について この記事は、 Insight Edge Advent Calendar 2025 1日目の記事です。 弊社には「 やってみる・みんなでやる・やりぬく 」というValueがあり、私たちテックブログ編集チームもこのValueを大切にしています。編集チームメンバーは年初にそれぞれ「やってみる」チャレンジを選ぶようにしており、今回紹介するレビューエージェントの進化は、まさにこのValueを体現した取り組みです。 1人のメンバーの「やってみる」から始まり、チーム全体で「みんなでやる」で改善を重ね、アドベントカレンダーという目標に向けて「やりぬく」——本記事では、そんなストーリーをお伝えします。 LangGraphからClaude Code Actionへ - Techblogレビューエージェントをみんなで進化させてみた1年間 このセクションでは、TechblogレビューエージェントをLangGraphベースからClaude Code Actionベースのマルチエージェント構成にシフトさせた過程を紹介します。現在のエージェント構成を先に知りたい方は、 次のセクション からご覧ください。 1. LangGraphでのエージェント作成からスタート(やってみる) 弊社のテックブログレビューエージェントは、以前も本テックブログで公開していた通り、Matsuzakiさんが LangGraphベースのレビューエージェント を作ってくださっていたことから始まりました。Techblogレビュー作業をエージェントを使って自動化する仕組みはここがスタートでした。 私も実際に活用させていただきましたが、人間が指摘しづらい細かいポイント(文言の統一性など)をずばっと指摘してくれて、とても助かりました。 当時のレビュー結果イメージも記事内にありますので、詳しくは Matsuzakiさんの記事 をご覧ください。 記事の中では、「今後やりたいこと」として以下のようなアイデアが挙げられていました。 記事内容に基づいて対象読者(ペルソナ)を作成し、ペルソナの視点からレビューする 内容チェックでWeb検索を取り入れ、類似記事の参照や比較ができるようにする レビューにSEO観点を取り入れ、検索流入を意識したキーワードやタイトルを提案する 2. Claude Code Actionも併用して活用してみる(みんなでやる) LangGraphベースのレビューエージェントを運用する中で執筆者や編集チームから多くのフィードバックが集まり、当時挙げられていた「やりたいこと」を実現したい思いも強くなっていきました。しかし、テックブログ編集チームは組織改善活動の一環であり、メンバーは日々の案件対応で忙しく、エージェントの改善やLangGraph実装を継続的に更新する時間を十分に確保するのは難しい状況でした。 ちょうどその頃、Claude CodeとClaude Code Action( 詳細はこちら )への関心が世の中に広まりました。編集チームのKさんが「Techblogの活動を自動化する」チャレンジを掲げており、すぐにもClaude Code Actionを導入していただき、Github上でClaude Codeによるレビューを試せる環境を整えてくれました。 この時は、レビュー観点を1つのプロンプトにまとめ、プルリクエスト上で @claude を呼び出すとClaude Codeが自律的にレビューしてくれる方針を採用していました。プロンプトは編集チーム全員で整理しました。誤字・脱字、読みやすさ、技術的正確性、記事の深さ、読者の引き込み、拡散性(SEO)などの観点でレビューするように指示しました。 プロンプトの詳細を見る ## タスク あなたはInsight Edge TechBlogの編集者です。Pull Requestの内容をもとにレビューを実施し、具体的な課題と修正方針を示す提案を作成します。 ## 手順 1. メンバーのTechblogをしっかり読み、内容を理解します。 2. 誤字、脱字がないかをチェックします。 3. 読みやすさに焦点を置き、チェックします。  - 言葉遣いや表現が明確で分かりやすいか? - 段落や見出しなどのフォーマットは整理されているか? - 図などで表現した方が分かりやすい項目がなかったか? 4. 技術的正確性に焦点を置き、チェックします。必要に応じてウェブ検索も行い、内容が正確かどうかを確認してください。 - 技術的な内容に誤りや誤解を招く表現はないか? - ソースデータが信頼できるか? 5. 記事の深さに焦点を置き、考えます。 - トピックに対する背景知識や文脈が十分に提供されているか? - 初心者から中級者・上級者までにとって有益な情報量か? 6. 読者の引き込みに焦点を置き、考えます。  - 冒頭から興味を惹かれる構成になっているか? - 読み進めたくなる工夫やストーリーテリングがあるか? 7. 拡散性に焦点を置き、考えます。 - タイトルなど検索でヒットしやすいワードが含まれているか?SEO向上のため、もっといい書き方がないか? - 記事の内容は読者から拡散したくなるような内容になっているか? 8. 何度か読み返し、同僚として親切な態度でレビューコメントを作成します。 ## 出力 - 形式: markdown形式で出力します。 - 構成: 以下の内容を含むレビューコメントを作成してください。絵文字なども使って分かりやすくしてください。 - 総評:記事を読んだ時の全体的な印象 - 良かった点 - 改善点 - 提案: 具体な箇所に対して具体的な対応方針か修正後の文章を提案してください。 このようなプロンプトでも、最初からいい感じのレビュー結果が返ってきました。例えば、先日公開された AIエージェントはなぜ複雑なタスクを完遂できないのか? の初稿でのレビュー結果は以下の通りです。 記事のレビュー結果イメージ 約半年間LangGraphとClaude Code Actionを併用してレビューを運用した結果、観点のアップデートのしやすさやClaude Codeの進化への期待から、編集チームでは最終的にClaude Code Actionベースへ一本化することを決めました。 継続運用で見えてきた課題と改善ポイント この仕組みを約半年間を運用してきましたが、執筆者や編集チームから以下のようなフィードバックがありました。 良かった点: 細かい誤字や文言の統一性、構成改善の提案、視覚化の提案、技術的な正確性、読みやすさの改善、SEO観点の提案など、人間が指摘しづらい点を遠慮なく指摘してくれました。 実際執筆していただいたメンバーからも以下のようなポジティブなフィードバックがありました。 メンバーの実際のフィードバック 一方で、レビュー記事数が増えてくるとだんだん以下のような課題も見えてきました。 課題と改善ポイント: レビューコメントの質にばらつきがある :コメントは長いものの、参考になる内容は7割ほどで、編集チームの最終レビューでは追加で指摘する箇所も残っていました。 ファクトチェックができていない :時事ネタ(セミナーの開催期間)、論文の内容、技術的な内容のファクトチェックができていなかった。プロンプトには「検索して確認するように」と書いていたのに、検索できている様子がなく、誤った指摘を作ってくることもあった 各観点のレビューが浅い :それぞれの観点である程度指摘はくるが、深くレビューできないため、深い指摘がこなかった 執筆フローに合わせたレビューができない :固定プロンプトのため、目次段階、初稿、修正後、どのタイミングでも毎回同じレビューをしていた さらに、課題とまではいかないものの、以下のような 追加でやりたいこと も見えてきました。 Matsuzakiさんが挙げていた ペルソナを作成してレビュー も取り入れるとレビューの質が上がるのでは? 過去記事との内部リンク最適化 :関連記事があれば自動で提案してくれると今年のチャレンジである「PV数向上」にもつながりそう 具体的な修正提案の提示 :該当箇所へのインラインコメントやサジェッション形式で、明確な修正案を直接提示してもらえるとより効率的では? 3. マルチエージェント構成への進化させてレビュー質を高める(やり抜く) それらの課題を解決したいという思いに加えて、Insight Edge初のアドベントカレンダーの開催もやってくる!月に25本の投稿をレビューしなければならないのは編集チームに対してかなりの負担になってしまうため、アドベントカレンダー企画者としてTechblogレビューエージェントの改善をやり抜く決意をしました。 そこで、以下の方針でエージェントを進化させることにしました。 1. 執筆フローに合わせた段階的レビュー 執筆者が必要なタイミングで必要なレビューを受けられるよう、3つのカスタムスラッシュコマンドを作りました。 /outline-review :目次段階で構成を簡潔にチェック /initial-review :初稿を徹底的にレビュー /update-review :修正後の差分をレビュー スラッシュコマンドの詳細は以下のClaude Codeの公式ドキュメントをご参照ください。 Claude Code公式ドキュメント: カスタムスラッシュコマンド 2. レビューの質を高める仕組み マルチエージェント化 :サブエージェント機能を使って各観点ごとに専門エージェントが深くレビューすることで、抽象的ではなく具体的なコメントを実現 ペルソナ駆動レビュー :読者体験評価エージェントには記事内容に応じてターゲット読者のペルソナを生成し、適用 MCPサーバーの設定 :外部ツールと連携し、最新の技術仕様の取得 Web検索、Web取得機能を有効化 :WebFetch、WebSearch機能を使えるようにすることで時事ネタや最新情報を検証 サブエージェント、MCPツール設定、Web検索・取得機能の設定詳細は以下のClaude Codeの公式ドキュメントをご参照ください。 Claude Code公式ドキュメント: サブエージェント Claude Code公式ドキュメント: MCPツール Claude Code公式ドキュメント: Claudeが利用できるツール 3. レビューの効率化 GitHub Suggestion機能 :Claude Code Actionのインラインコメント作成MCPツールを活用し、該当箇所に直接修正案を提示してワンクリックで修正を適用可能にする 現在のTechblogレビューエージェント詳細 では現在のTechblogレビューエージェントがどうなっているのか、説明します。 レビューエージェント構成 全体のアーキテクチャは以下の通りです。 Techblog Review System アーキテクチャ(Google Nano Banana Proを使用して生成) ちょっと複雑に見えるかもしれませんが、流れはシンプルです。 スラッシュコマンド実行: /outline-review 、 /initial-review 、 /update-review のいずれかのコマンドをGitHub上で実行 メインエージェント(Claude Code)による処理: 記事を分析し、必要なサブエージェントを選択して順次か並列で起動 サブエージェントによる専門レビュー: 各エージェントが独立したコンテキストで並列処理 結果統合・出力: 環境に応じてMarkdownファイルまたはPRコメントとして出力 今回は、以下のサブエージェントを用意しています。 サブエージェント名 主な役割 起動条件 ペルソナ適用 使用MCPツール persona-generator ペルソナ生成 常時 - - structure-evaluator 構成・深さ・引き込み評価 常時 ✓ - japanese-quality-checker 日本語品質チェック 常時 ✓ textlint MCP quality-checker フォーマット・視覚化チェック 常時 × - seo-analyzer SEO最適化 常時 ✓ - internal-link-optimizer 内部リンク提案 常時 ✓ - tech-validator 技術的正確性の検証 条件付き* × Context7 MCP, WebSearch fact-checker 時事ネタ・統計データ検証 条件付き** × WebSearch compliance-checker コンプライアンス・法的リスク 常時 × - *技術内容が深い場合(論文引用、技術用語10個以上、コードブロック3つ以上など)のみ起動 **外部リンクや時事的事実(「最近」「先日」「発表」「リリース」など)が含まれる場合に起動 各サブエージェントは独立したコンテキストで動作するため、それぞれの専門分野に集中した深いレビューが可能になっています。 記事レビュー例 では実際の私のこのブログでレビューしてみます。 執筆者が目次・ドラフトか原稿をプルリクエストに挙げた際、編集チームメンバーがプルリクエストのコメント上で @claude /outline-review や @claude /initial-review を実行してレビューを依頼します。 GitHub上でのスラッシュコマンド実行イメージ /outline-review の実行結果 まずは目次を上げた段階で /outline-review を実行してレビューをお願いしていました。 アウトラインレビュー結果 SEO対策のため、タイトルを短くするようにと提案されたり、流入を増やすため関連記事のリンクを追加するように提案されたりしていますね。 この後、全部指摘を採用させていただき、改善しました! ちなみにレビュー過程では、以下のようなペルソナを作成してくれたようです。 ペルソナ生成結果 /initial-review の実行結果 次に、初稿を完成し、 /initial-review を実行してレビューをお願いしました。 初稿レビュー結果 ー 総合コメント 初稿レビュー結果 ー GitHub Suggestion 総合コメントから分かる通り、とても詳細にレビューできていると思います。 また、インラインコメントで改善提案も色々もらえました。 アドベントカレンダーをどうしても最初から宣伝したかったのに・・・エージェント的にはあまり良くないようですね。笑 工夫点 次に、このレビュー仕組みを構築した時の工夫ポイントを紹介させていただきます。もし同じような仕組みを作りたい方がいれば、参考になれば嬉しいです。 カスタムスラッシュコマンドのワークフロー制御でレビューフローの最適化 カスタムスラッシュコマンドは簡単にいうと事前に定義されたプロンプトで、コマンド呼び出し時にこのプロンプトがClaude Codeのメインセクションに渡され、指示された処理が実行されます。 例えば初稿をレビューするための /initial-review コマンドでは、環境判定 → レビュー対象特定 → ペルソナ生成 → 記事分析 → サブエージェント選択・並列起動(ペルソナ情報も渡す)→ 結果統合 → 環境別出力という流れで動けるように、カスタムスラッシュコマンドの定義に工夫しました。 初稿レビュー用カスタムスラッシュコマンドワークフローイメージ ー Google Nano Banana Proを使用して生成 具体的な工夫ポイントを以下に紹介します。 環境判定と出力先の切り替え: GitHub Actions環境かローカル環境かで出力先を切り替えるようにしました。 if [ "$GITHUB_ACTIONS" = "true" ]; then echo "レビュー結果はPRコメントとして投稿されます" else echo "レビュー結果は.reviewsフォルダーに保存されます" fi この目的は、執筆者が執筆中でもローカル環境で自身の記事をレビューできるようにするためです。これによって編集チームのレビュー負担も軽減できます。 ペルソナ駆動レビュー: ワークフロー開始時に記事の内容を理解し、対象読者と想定するペルソナを作成。これらのペルソナを読者体験評価エージェント(構成、日本語品質、SEO、内部リンク)に適用し、多角なレビューをさせるように工夫しました。一方で、技術的正確性や法的リスクなど客観的に判断すべき観点はペルソナに依存せず、専門家視点で評価させるようにしました。 **ペルソナの適用:** 生成されたペルソナは、以下の**読者体験評価エージェント**に適用してください。 - ` structure-evaluator ` : 読者にとって理解しやすい構成か? - ` japanese-quality-checker ` : 読者にとって読みやすい日本語か? - ` seo-analyzer ` : 読者の興味を引くタイトルか? - ` internal-link-optimizer ` : 読者が次に読みたい記事へ誘導できているか? **重要:** これらのエージェントは、**生成された各ペルソナごとに個別に起動**してください。 - 例: 3つのペルソナが生成された場合、 ` structure-evaluator ` を3回(各ペルソナで1回ずつ)起動 - 各起動時には、該当するペルソナ情報を渡してください 以下のエージェントは、プロの専門家視点で客観的に評価します(**ペルソナ不要、1回のみ起動**)。 - ` tech-validator ` : 技術的正確性 - ` fact-checker ` : 事実確認 - ` compliance-checker ` : 法的リスク - ` quality-checker ` : 視覚的品質 条件付きサブエージェント起動: 記事内容によってチェック観点は変わるため、条件付きでサブエージェントを起動するようにしました。 **条件付きエージェント:** - ` tech-validator ` : 技術内容が深い場合のみ起動 - 論文の引用、arXiv/DOIリンク、技術用語10個以上、コードブロック3つ以上などがある場合 - ` fact-checker ` : 以下のいずれかに該当する場合に起動 - 外部リンク(http/https)が含まれている - 時事的事実がある(「最近」「先日」「発表」「リリース」などのキーワード) これによって全ての記事に対して無駄に全ての観点でチェックする必要がなくなり、効率的かつコスト抑えてレビューできるようになります。 並列実行の明示的な指示: 各サブエージェントは独立とした観点でチェックし、順序関係がないため、並列で起動するように指示しました。 ### 3. サブエージェント並列実行 選定したサブエージェントを並列で起動してください。 これによりGitHub Actionの実行時間も短縮でき、コスト削減にもつながります。 サブエージェント分離によるコンテキスト節約とレビューの深さ向上 上でも書いた通り、Claude Code Actionを導入した当初は1つの大きなプロンプトで全観点をレビューしていました。しかし、各観点でのレビューが浅く、具体的な指摘が少ないという課題がありました。そこで、Claude Codeのサブエージェント機能を活用し、観点ごとに独立したエージェントへ分離してみました。サブエージェントは独立したコンテキストウィンドウで動作するため、それぞれが専門分野へ特化したシステムプロンプトと十分なコンテキストを持つことができます。 これにより、例えば japanese-quality-checker は日本語品質チェックのベストプラクティスやtextlintの使い方に集中でき、 tech-validator は技術検証に必要な詳細な指示を持つことができるようになりました。結果として、各観点でのレビューの深さが向上したと思います。 ※ 階層型マルチエージェントは AIエージェントが複雑タスクを完遂できない理由と、「マルチエージェント×コンテキストエンジニアリング」の最新手法 でも説明されていますので、ぜひ合わせてご覧ください。 サブエージェントからメインスレッドへの結果受け渡し:JSON設計の工夫 複数のサブエージェントを並列実行する際、各エージェントからの結果をどう受け取り、どう統合するかが課題でした。 最後の総合コメントおよびサジェッション投稿を統合して整理しやすいように、現在では各サブエージェントには以下のような統一されたJSON形式で結果を返すよう指示しています。 { " category ": " 日本語品質 ", " issues ": [ { " type ": " 誤字 ", " severity ": " high ", " location ": " 第3章 ", " problem ": " 「てにをは」の誤り ", " suggestion ": " 具体的な修正案 ", " original_text ": " 元のテキスト ", " suggested_text ": " 修正後のテキスト ", " line_range ": " 105-106 " } ] , " inline_comments ": [ { " comment_type ": " suggestion ", " line_range ": " 120 ", " comment ": " コメント内容 " } ] , " positives ": [ " 良かった点 " ] , " summary ": " 総評 " } この設計により、メインスレッドでは各エージェントのJSON結果を受け取り、以下の処理が可能になりました。 重要度( severity )別に指摘事項を整理 original_text 、 suggested_text 、 line_range が揃っている指摘を自動的にGitHub Suggestionとして投稿 カテゴリ別に整理された最終レポートの生成 GitHub Suggestion機能を確実に動かすための指示の工夫 Claude Code ActionにはGitHub PR上でインラインコメントを投稿する mcp__github_inline_comment__create_inline_comment というMCPツールが用意されています。 しかし、コマンドやサブエージェントのプロンプトで明示的に「このツールを使え」と指示しないと、使われないことも多いです。そのため、明示的に使うよう指示しました。 **IMPORTANT - GitHub PR Inline Comment形式での出力:** この実行はGitHub Actions環境で行われています。 具体的な修正案がある場合は、必ず以下のフィールドを含めてください。 - ` original_text ` : 修正対象の元のテキスト - ` suggested_text ` : 修正後のテキスト - ` line_range ` : 該当箇所の行範囲 これらの情報を ` mcp__github_inline_comment__create_inline_comment ` ツールを使って GitHub PR上でSuggestion形式のインラインコメントとして投稿してください。 なお、Claude Code Actionがデフォルトで mcp__github_inline_comment__create_inline_comment を許可していない可能性もありますので、許可するように設定を追加しました。 MCPツールの積極的な活用 レビューの質を高めるため、以下のMCPツールを活用しています。 textlint MCP ( mcp__textlint__lintFile ):日本語の自動チェックに使用。 japanese-quality-checker が呼び出し Context7 MCP ( mcp__context7__get-library-docs ):最新の技術ドキュメント取得に使用。 tech-validator が呼び出し github_inline_comment MCP ( mcp__github_inline_comment__create_inline_comment ):GitHub PR Suggestionの投稿に使用。Claude Code Action内で用意されたMCPツールのため、設定不要。 Web検索・取得機能の有効化 とても簡単なことですが、以前の仕組みではプロンプトで必要に応じてウェブ検索するように指示していたにもかかわらず、事実確認が必要な指摘事項はほとんどハルシネーションが発生して、プロンプトが悪いのか?とずっと思い込んでいました。しかし、実はClaude Code Actionはデフォルトでこの機能を許可していないため、検索や取得ができていなかったことが最近判明しました。笑 そのため、今回からは WebSearch と WebFetch 機能を有効化することで、 tech-validator や fact-checker が最新の公式情報を検証できるようにしています。 Claude Code Actionの設定は以下としました。 - name : Run Claude Code Actions Review uses : anthropics/claude-code-action@v1 with : use_bedrock : "true" track_progress : true github_token : ${{ steps.app-token.outputs.token }} claude_args : | --model sonnet --max-turns 50 --mcp-config .mcp.json --allowedTools Task,Edit,Read,WebFetch,WebSearch,Glob,Grep,SlashCommand,mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,mcp__context7__resolve-library-id,mcp__context7__get-library-docs,mcp__textlint__lintFile,mcp__textlint__lintText,mcp__textlint__getLintFixedFileContent,mcp__textlint__getLintFixedTextContent,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(git log:*),Bash(git diff:*),Bash(git branch:*) --disallowedTools "" prompt : | (省略します) 残っている課題と今後の改善点 上記の仕組みを導入してから、現在のところメンバーからかなりポジティブなフィードバックをもらっていますが、まだいくつかの課題が残っています。 サブエージェントによってはまだ過剰や不足な指摘がある。 → 今後は編集チーム全員で継続的にプロンプトを改善していこうと思います。 サブエージェントはなるべく並列実行をするようにしているが、それでも実行時間は前より長くなっているため、Github Actionのコストがかかる。 → 必要なエージェントのみ起動する判定ロジックをさらに洗練させたいですね。 現在はWeb検索・取得機能を特に制限なく有効化してしまっていますが、むやみに外部情報を参照しすぎると、コンテキストも増大し、コスト増加やハルシネーションのリスクもある。 → 今後適切な制限を設けたいと考えています。 また他の課題もあると思いますが、これから皆さんのフィードバックを求め続け、改善を続けていきたいと思います。 まとめ テックブログレビューエージェントは、Matsuzakiさんの「やってみる」から始まり、Kさんや編集チームメンバーの「みんなでやる」で改善、そしてアドベントカレンダーに向けた「やり抜く」で改善を続けてきたInsight Edgeらしい事例を紹介させていただきました。 私個人もこの活動を通じてClaude Code Actionの機能、Claude Codeのスラッシュコマンド、サブエージェントに関してより詳しくなりました。このレビューの仕組みはTechblogに限らず、案件のコードレビューやドキュメントレビューなどにも活用できると思いますので、この知見を活かして社内のコードレビュー課題を解決することにも挑戦してみたいと思います。そこでもう1つの「やってみる・みんなでやる・やりぬく」のValue事例が作れたら嬉しいです。 Insight Edge Advent Calendar 2025が始まりました! 最後に、改めてお知らせです! この記事は Insight Edge Advent Calendar 2025 の1日目として公開しています。 12月25日まで毎日、弊社メンバーがそれぞれの挑戦や学びを発信していきますので、ぜひ Insight Edge Advent Calendar 2025 をフォローして、お楽しみください!
MathJax = {tex: {inlineMath: [['$', '$']]}}; Insight Edgeのデータサイエンティストのki_ieです。数理最適化の専門家として、これまでさまざまな課題を数理最適化問題としてモデリングしてきました。 モデリングはアルゴリズム設計と比べて注目を集めることが少ないようですが、実際には技術的な知見・調査を要求する骨の折れるタスクです。 このタスクを賢いLLMが手伝ってくれたら嬉しいですね! 昨年の記事 では ChatGPT の OpenAI o1 にどれだけ数理最適化問題のモデリングを任せられるか試してみました。今回の記事では最新の ChatGPT モデルである GPT-5 Thinking を使って同様の実験を行い、どこまで使えるものになったのかを確認します。 数理最適化に詳しい方は、準備的な内容をスキップして 前回のおさらいと今回の狙い から読み始めていただければと思います。 数理最適化問題とは 数理最適化問題と混合整数計画問題の基礎知識がある方はこの節はスキップしましょう(面白いことは一つも書いてありません)! このセクションは 昨年の記事 と全く同じ内容です。 数理最適化問題とその実行可能解・最適解 数理最適化問題とは「変数・制約・目的関数」が与えられたときに、 「制約を守るなかで最も目的関数を小さく(または大きく)する決定変数を選ぶ」 という問題です。 数理最適化問題は、一般に以下の形で表現できます: 数理最適化問題 変数 : $x \in D \ \ (\subseteq \mathbb{R}^n)$ 変数 $x$ は $D$ の中から選べる 制約 : $g_i(x) \leq b_i \ \ (i \in I)$ $x$ は $g_i(x) ≤ b_i$ がすべての $i \in I$ について成り立つように選ぶ 目的関数(最小化) : $\min f(x)$ 上記ルールを守ったうえで $f(x)$ を最小化したい。 上手に $D$, $g_i$, $f$ を設計してやることで、配送計画から証券ポートフォリオの最適化まで幅広い問題が 数理最適化問題として表現できることが知られています。 たとえばコンビニの店長の立場でおにぎりの仕入れ量を適正化する問題を単純化すれば、以下のような最適化問題としてモデル化できます: 例: おにぎりの仕入れ量の適正化 変数: おにぎりの仕入れ量 $x \in \mathbb{Z}_+$ 制約: おにぎりが店から溢れない $x \leq 100$ 目的関数: $\max f(x)$ $f(x)$ : おにぎり $x$ を仕入れたときの期待利益 $D$ の要素で制約をすべて守るものはその問題の実行可能解と呼ばれます。 実行可能解のうち目的関数を最小にするものは数理最適化問題の最適解(または解)と呼ばれます。 数理最適化問題で課題をモデリングする目的は、最適解を得ることにあります。 たとえばおにぎりの仕入れ量の適正化の文脈では最も利益があがる仕入れ量が $70$ であれば、$x=70$ が最適解です。 これが計算で求められれば、このコンビニでは利益を最大化する仕入れの意思決定ができるようになってハッピーです。 一方、問題によっては最適解を求めることが極めて難しいことがあります。 おにぎりの仕入れ量の適正化問題は$x=0, 1, 2 \cdots 100$ を全探索すれば簡単に最適解が求まりますが、 もっと複雑な問題ではそうはいかないこともしばしばあるのです。 そのような場合は「目的関数値の良い」実行可能解が求まったらそれでよしとすることが実務上は一般的です。 おにぎりの仕入れ量の問題で言えば、$x=68, 71$ といった解が見つかって、$x=70$ が見つけられなかったら、 $x=68, 71$ をとりあえずの答えとして採用するというイメージです。 この場合でも、あてずっぽうで仕入れ量を決定するコンビニよりは利益を大きくできるわけです。 混合整数計画問題 数理最適化問題の中でも特別に重要な問題クラスとして、混合整数計画(MIP; Mixed Integer Programming)問題があります。 これは実行可能領域 $D$ を$\mathbb{R}^n \times \mathbb{Z}^m$ として、制約・目的関数を線形なものに限定したものです。 混合整数計画問題 変数 : $x \in \mathbb{R}^n \times \mathbb{Z}^m$ 制約 : $a_i^\top x \leq b_i \ \ (i \in I)$ $a_i^\top$ を行ベクトルとして集めた行列を$A$, $b_i$ を集めたベクトルを $b$ としたら、まとめて $Ax ≤ b$ と書ける。 目的関数(最小化) : $\min c^ \top x$ 変数に別途上下限を許す場合も、等号制約を許す場合もある。いずれもここで示した形に容易に変形できる この問題クラスの重要性を説明するために、まず数理最適化を利用した課題解決の一般的な流れを説明します。 さまざまなタイプの数理最適化問題に対して、最適解または最適解ではないにせよ目的関数値の良い実行可能解を求めてくれるプログラムが作られています。これらのプログラムはよく「ソルバー」とよばれます。数理最適化の考え方とソルバーを利用して現実の課題を解くときの流れは以下のようになります。 課題の整理 課題を数理最適化問題としてモデル化 数理最適化問題をソルバーで求解する 得られた最適解(または目的関数値の良い実行可能解)を現実世界で採用する 課題を数理最適化問題に落とし込むことにも技術的な難しさがありますが、 その数理最適化問題を解くソルバーを設計・実装することもまた大変に難しいことです。 そのため、できれば既存のソルバーを用いて求解できるような範囲でモデル化をしたいというのが数理最適化エンジニアの本音です。 MIPによるモデリングはこの観点で大変すぐれています。MIPは多様な問題を表現でき、さらに優れたソルバーが存在するからです。 MIPは制約・目的関数が線形に限定されているため一見すると限られた表現力しかないように見えますが、0, 1の値しかとらないバイナリ変数を巧みに使うことで複雑な論理関係を表現でき、幅広い問題を表現可能です。表現できる問題の幅広さゆえ、学術的にも深く研究され、ソルバーの研究が積み重ねられてきました。その結果、優れた商用・OSSのソルバーが存在するのです。 このような背景から、MIPによるモデリングは数理最適化の実務においてなくてはならない基本技術となっています。 LLMに混合整数計画問題をモデリングさせたいモチベーション このセクションは 昨年の記事 と全く同じ内容です。 課題のMIPモデリングというのはなかなか骨の折れる作業です。 論理的に正しいというだけではなくソルバーにとって解きやすいモデルを作ることが求められるため、 難しい課題の場合は最適化モデルの試行錯誤・調査の1サイクルだけで数日かかってしまう、ということも稀ではありません。 この作業、なるべくサボりたいですね。性能向上の著しいLLMに手伝ってもらえれば、 まるごと作業代替とはならなくとも効率化できるのではないか?というのが、本記事で検証したいポイントです。 数理最適化エンジニアとして確認したい具体的なポイントは以下の3つです。 有名問題をモデル化できるか / 有名定石を利用できるか? 有名問題に簡単な要件を追加してもモデル化がうまくいくか? 複雑な要件を上手にモデル化できるか? LLM に 1. ができたら、数理最適化初心者/学習者にはLLMによる補助が有用だと考えられるでしょう。 数理最適化問題のモデリングにはさまざまな「定石」が登場します。 例えば、0/1変数 $x, y \in \{0, 1 \}$ の論理積をMIPの枠組みで考えたいときには、補助変数 $z \in \{0, 1 \}$ を導入して 制約 $z ≤ x, z ≤ y, x+y-1≤z$ を加えれば $z$ は $x, y$ の論理積(AND)となる、というテクニックがあります。 知っていれば簡単に使えるテクニックですが、自分で思いつくのは難しいです。 初心者がこれをLLMに教えてもらえるなら、学習効率は上がりそうですね。 ある程度経験のある数理最適化エンジニアにとっても、モデル化方法の検討がつかないときのサーベイの代替として LLMに聞くというのが有用になるかもしれません。 2.ができたら、実務的なモデリングの補助としても使えそうです。 実際の問題は有名定石単体で解けないケースのほうが多く、 そのような場合定石を組み合わせたり、自分でテクニックを作ってモデル化する必要があります。 ここまでLLMがやってくれるなら、実務的なモデリングの補助として十分有用でしょう。 3.は、ここまでできたらモデリングの初手はLLMとの対話になりそうですね。 これで毎回完璧な答えが出てきたら私達は冷や飯を食わされることになりそうですが…どこまでできるか見てみましょう。 前回のおさらいと今回の狙い 昨年の記事では o1-mini に3つの難易度のモデリングタスクを実施させました。その結果、当時のLLMは以下のレベルの能力を持つとわかりました。 有名問題をモデル化できるか / 有名定石を利用できるか? ▶ できる 有名問題に簡単な要件を追加してもモデル化がうまくいくか? ▶ ちょっとできる 複雑な要件を上手にモデル化できるか? ▶ できない 難易度 1 の実験では、 LLM に有名な数理最適化問題 (巡回セールスマン問題とクラス分け問題) の要件を日本語で与え、正しくモデリングができるかを確認しました。いずれについても o1-mini で問題のないモデリングが得られたため、今回は追加実験をしません。詳細が気になる方は、 昨年の記事 を参照してください。有名問題は正解の数理モデルをLLMが知識として知っているので、これはモデリング能力の証明というより知識の再生能力が示されたと考えたほうがいいかもしれません。 有名問題に簡単な要件を追加した難易度 2 の実験では、大筋は良いものの多少のツッコミどころがあるという出力で、 有名問題と有名手法の簡単なパッチワークではモデル化できない課題を扱う難易度 3 の実験では全く歯が立たない、という結果でした。 今回の記事では最新の GPT-5 Thinking を用いてレベル2, レベル3のタスクを再度試み、どの程度レベルが上っているのかを確認します。 LLM に数理最適化問題をモデリングさせる 2. 有名問題に簡単な要件を追加してもモデル化がうまくいくか? 「1. 有名問題をモデル化できるか / 有名定石を利用できるか?」はスキップするので 2. からナンバリングを始めています。 巡回セールスマン問題 + α 巡回セールスマン問題に追加要件を加えた問題を GPT-5 Thinking に投げて、モデリングが正しくできるか見ていきます。 太字部分 が通常の巡回セールスマン問題にはない追加要件です。 次の課題を混合整数計画問題としてモデル化してください。 「10個の都市をすべて回って出発地点に返ってくる必要がある。移動時間を最短化したい。 ただし、特定の3都市を先に訪問し、残りの7都市を後に訪問したい。 」 結果は「パラメタ」「決定変数」「制約」「目的関数」の4セクションに分けてください。 結果は次の通りです(主結果の前後のテキストの削除とフォーマット修正は出力後編集): パラメタ 都市集合:$ V = \{1, \dots, 10\} $ 出発・到着の仮想ノード:$ 0 $(都市数に含めない) 先行訪問集合:$ A \subset V,\ |A| = 3 $ 後行訪問集合:$ B = V \setminus A,\ |B| = 7 $ 移動時間(または距離):$ c_{ij} \ge 0 \quad (i, j \in V \cup \{0\},\ i \ne j) $ 都市数:$ n = |V| = 10 $ 決定変数 経路採用:$ x_{ij} \in \{0,1\} \quad (i, j \in V \cup \{0\},\ i \ne j) $ $ x_{ij} = 1 $ なら弧 $ i \to j $ を通る 訪問順序指標:$ u_i \in \{1, \dots, n\} \quad (i \in V) $ 制約 出発点・終点 $$ \sum_{j \in V} x_{0j} = 1, \qquad \sum_{i \in V} x_{i0} = 1$$ 各都市の入次数・出次数 $$ \sum_{\substack{j \in V \cup \{0\} \\ j \ne i}} x_{ij} = 1 \quad (\forall i \in V), \qquad \sum_{\substack{j \in V \cup \{0\} \\ j \ne i}} x_{ji} = 1 \quad (\forall i \in V)$$ 部分巡回消去(MTZ制約) $$ u_i - u_j + n\,x_{ij} \le n - 1 \quad (\forall i, j \in V,\ i \ne j)$$ 先行訪問制約(3都市を必ず先に訪問) $$ u_i \le u_j - 1 \quad (\forall i \in A,\ \forall j \in B)$$ 変数の領域 $$ x_{ij} \in \{0,1\} \ (i \ne j), \quad u_i \in \{1, \dots, n\}$$ 目的関数 $$\min \sum_{i \in V \cup \{0\}} \sum_{\substack{j \in V \cup \{0\} \\ j \ne i}} c_{ij} \, x_{ij}$$ ほぼ完璧です。前回の記事では地点 1 を出発点と固定したモデルを出力してしまいましたが、今回は仮想的な始点 $0$ を導入することで 出発地点の自由度を保っています。巡回セールスマン問題では、ぐるっと一周するという性質から出発地点を好きに再解釈できるため、出発地点を固定してかまわないのですが、この問題では「先行訪問集合」が定義されているため、出発地点を固定してしまうと問題があります。この点に気づいて対応できているのは素晴らしいです。 (前回の記事では「固定しちゃってるけどまあ固定した問題としては正しいかな」と思って見過ごしていたのですが、冷静に考えると入力された課題文には忠実ではないわけで、指摘するべきでした!前回記事読者のみなさま、失礼しました。) その他の面も問題なさそうです。o1-mini で少し怪しい記述になっていた $u_i$ の値域(整数vs連続)についても、今回は整数変数として揺らぎなく書かれています。個人的には連続変数と書いてくれたほうが「わかっている」感が出せてると思いますが、 モデリングとしては問題ないでしょう。 問題を指摘するとすれば「変数の領域」で値域が繰り返し書かれているのは記述として気になりますね。 また「先行訪問制約」の記述が冗長なのは効率が悪い記述で、 $u_i \leq |A| (i \in A)$ などと効率的に書いてほしいものです。間違いとは言えませんが、人手での修正が必要ですね。 総合評価としては「人力の確認と修正が入る前提であれば、有用な出力を出せている」という前回と同様の結果だと思いますが、 より一貫した、修正もしやすい良いモデルになっていると思います。 特に仮想的な始点を導入できているのは素晴らしいです。これは仕事で使えるツール感が出ていますね。 クラス分け問題 + α 有名なクラス分け問題に追加要件(太字部分)を入れた次の発展編も解かせてみましょう。 GPT-5 Thinking へ次の質問を投げます。LLM に喧嘩を防いでもらいましょう。 次の課題を混合整数計画問題としてモデル化してください。 「120人の学生を3つのクラスに分けたい。3つのクラスの人数は等しくしたうえで、クラス間の成績のばらつきを最小に抑えたい。 ただし、何対かの喧嘩をしてしまう生徒ペアがあるため、それらのペアの生徒は必ず別のクラスにしてほしい。 」 結果は「パラメタ」「決定変数」「制約」「目的関数」の4セクションに分けてください。 次の結果が返ってきました(主結果の前後のテキストの削除とフォーマット修正は出力後編集): パラメタ 学生集合 $I={1,\dots,120}$ クラス集合 $C={1,2,3}$ 成績 $s_i \in \mathbb{R}$(各学生 (i) の既知のスコア) 喧嘩ペア集合 $\mathcal{A} \subseteq \{(i,j)\in I\times I \mid i<j\}$ 各クラスの定員 $Q=40$(固定) 全体平均 $\bar{s}=\frac{1}{120}\sum_{i\in I} s_i$(定数) 決定変数 $x_{ic}\in {0,1}$: 学生 $(i)$ をクラス $(c)$ に割り当てるなら1 $u_c \in \mathbb{R}$: クラス $(c)$ の平均成績 $z \ge 0$: クラス平均の最大偏差の上限 制約 各学生はちょうど1クラス $\sum_{c\in C} x_{ic}=1 \quad \forall i\in I$ 各クラスの人数は等しい $\sum_{i\in I} x_{ic}=Q \quad \forall c\in C$ 喧嘩ペアは同一クラス禁止 $x_{ic}+x_{jc}\le 1 \quad \forall (i,j)\in \mathcal{A},\ \forall c\in C$ クラス平均の定義 $Q u_c=\sum_{i\in I} s_i x_{ic} \quad \forall c\in C$ クラス平均の偏差を $(z)$ で抑制 $u_c-\bar{s}\le z,\quad \bar{s}-u_c \le z \quad \forall c\in C$ 目的関数 クラス間の成績ばらつき(クラス平均の最大偏差)を最小化 $\min\ z$ 完璧です。この問題については前回もほぼ問題ない答えでしたが、重箱の隅をつつくような指摘はできました。今回は文句無しに完璧です。 3. 複雑な要件を上手にモデル化できるか? ここまで、有名問題に毛が生えた程度の問題であれば GPT-5 Thinking はそれなりに正しくモデル化してくれることがわかりました。 最後にしっかりと複雑な要件を与えて、モデル化できるか見てみます。 GPT-5 Thinking へ次の質問を投げます。 次の課題を混合整数計画問題としてモデル化してください。 「拠点にある一台の車を使ってすべての店舗を回って拠点に戻る計画を立てたい。 拠点と各店舗にはそれぞれタイムウィンドウが設定されている。 各店舗のタイムウィンドウは最大3つであり、作業開始時間がいずれかのタイムウィンドウに入っていれば許されるものとする。 拠点のタイムウィンドウは1つであるが、出発時刻・帰還時刻のいずれもここに収まる必要がある。 巡回中のいずれかのタイミングで1度だけ拠点への報告業務を行うため、ここで一定の時間を使う必要がある。 報告業務はいずれかの地点での作業終了直後に行うものとする。 報告業務を行うことができるタイムウィンドウは1つ指定されているものとする。 各店舗での作業時間・地点間の移動時間・報告業務の時間はパラメタで与えられる。」 結果は「パラメタ」「決定変数」「制約」「目的関数」の4セクションに分けてください。 結果は次の通りです (主結果の前後のテキストの削除とフォーマット修正は出力後編集): パラメタ ノード集合: 拠点出発 $0$ 店舗集合 $N=\{1,\dots,n\}$ 拠点帰着 $n{+}1$ 許可アーク集合 $A=\{(i,j)\mid i\in\{0\}\cup N,\ j\in N\cup\{n{+}1\},\ i\neq j\}$。 走行時間(対称でなくてよい):$\tau_{ij}\ge 0$ for $(i,j)\in A$。 作業時間:$s_i\ge 0$ for $i\in N$。拠点は $s_0=s_{n+1}=0$。 店舗タイムウィンドウ(高々3本):店舗 $i\in N$ に対し区間集合 $K_i$($|K_i|\le 3$)と各区間 $[a_{ik},\,b_{ik}]$。 拠点タイムウィンドウ(1本):$[a_0,\,b_0]$(出発時刻 $t_0$ と帰還時刻 $t_{n+1}$ の双方がこの範囲)。 報告業務: 所要時間 $r\ge 0$ 実施可能タイムウィンドウ $[a^R,\,b^R]$(開始時刻がこの範囲)。 ビッグ M: 時間緩和用 $M := (b_0-a_0)+s_{\max}+\tau_{\max}+r$($s_{\max}=\max_i s_i,\ \tau_{\max}=\max_{(i,j)\in A}\tau_{ij}$) 報告窓用 $M_R := (b_0-a_0)+s_{\max}$ 決定変数 ルーティング:$x_{ij}\in\{0,1\}$ for $(i,j)\in A$(アーク使用なら1)。 到着(作業開始)時刻:$t_i\in\mathbb{R}_{\ge 0}$ for $i\in\{0\}\cup N\cup\{n{+}1\}$。 窓選択:$w_{ik}\in\{0,1\}$ for $i\in N,\ k\in K_i$(店舗で採用する窓を1本選択)。 報告実施場所選択:$y_i\in\{0,1\}$ for $i\in N$(店舗 $i$ の作業直後に報告を行うなら1)。実施は1回のみ。 制約 (1) 流れ・訪問制約(単一車両で全店舗1回訪問) $\sum_{j\in N\cup\{n{+}1\}} x_{0j}=1,$ $\sum_{i\in \{0\}\cup N} x_{i,n{+}1}=1,$ $\sum_{j\in N\cup\{n{+}1\}} x_{ij}=1\ \ (\forall i\in N),$ $\sum_{i\in \{0\}\cup N} x_{ij}=1\ \ (\forall j\in N)$ (2) 時間伝播(作業・報告・移動を含む) $t_j \ge t_i + s_i + \tau_{ij} + r\,y_i - M\,(1-x_{ij})\ \ \ (\forall (i,j)\in A)$。 ($s_0=s_{n+1}=0$。 報告は店舗でのみ起こり $y_i$ は $i\in N$ のみ。 ) (3) タイムウィンドウ 拠点: $a_0 \le t_0 \le b_0,\ \ a_0 \le t_{n+1} \le b_0$。 店舗(窓の選択と整合): $\sum_{k\in K_i} w_{ik}=1$ $t_i \ge \sum_{k\in K_i} a_{ik}w_{ik} (\forall i\in N)$ $t_i \le \sum_{k\in K_i} b_{ik}w_{ik}\ (\forall i\in N)$。 (4) 報告業務は1回のみ・直後開始・報告窓内 $\sum_{i\in N} y_i = 1$, $t_i + s_i \ge a^R - M_R(1-y_i),\ \ t_i + s_i \le b^R + M_R(1-y_i)\ \ (\forall i\in N)$。 (作業終了直後に開始するので開始時刻は $t_i+s_i$。) (5) 自己ループ禁止 $x_{ii}=0\ \ (\forall i\in N)$。 目的関数 帰還時刻の最小化(最短完了): $\min\ t_{n+1}$。 (距離最小化を望む場合は $\min \sum_{(i,j)\in A}\tau_{ij}x_{ij}$ に置換可能。) おお、これは…!なんだかぱっとみは良さそうですね!各セクションを見ていきましょう。 パラメタセクションでは、 $K_i$ が区間の集合のように書いてあります。これは制約(3)の $k \in K_i$ などでみられる $K_i$ を添え字の集合と見る立場と異なり、形式的には問題がありますね。その他は大きな問題はないように見えます。big-M の具体的な値に問題がないかは、実際に制約で登場するところで確認しましょう。 制約セクションは(1)〜(5)までに分かれて記述されています。わかりやすくていいですね。 (1), (2) ではサイクル除去を含めた巡回セールスマン問題の制約が書かれています。MTZとは少し違う形式ですが、 (2) があることでサイクル除去まで対応できているのですね。厳密には $s_i + \tau_{ij} + r=0$ となるケースでサイクルが除去できなかったり、計算効率の観点から $u$ を導入する普通の MTZ 制約を入れるという考え方もありえると思いますが、問題があるとしても修復可能なものだと思っていいでしょう。 big-M として登場した $M$ の値も、う〜ん多分これは正しいですね。すごい。(2) の最後の括弧の補足部分は蛇足ですが、これは無視すればいいでしょう。 (3) はタイムウィンドウに関しての制約です。拠点に関するものは正しいですね。店舗に関するものは、$w_ik$ を一つ選ぶということはいいのですが、$t_i \leq ...$ の制約は明確におかしいですね($w_ik = 0$ のときに過剰に強い制約)。 $t_i \geq ...$ のほうも、登場する時刻の値がすべて非負という前提がなければ同様におかしいモデリングです。簡単に修正可能ですが、ミスではありますね。 (4) 報告に関する要件を記述しています。$M_R$ の具体的値の正しさは後回しにしましょう。このモデリングの方針は正しいですね! これができるなら (3) でも同じ手法を使ってほしかったですが、こういうミスって人間もしちゃいますね。(4) で正解を出してきたので許せます。 $M_R$ の値は、報告タイムウィンドウが拠点タイムウィンドウに含まれるという前提のもとでは問題はない気がしますが、すこしゆるい評価になっている気がします。これは些末な問題で、ちゃんと計算すれば修正可能です。 (5) は (1) にまとめてもいいでしょう。問題ありません。 これは素晴らしい進化です。前回の実験では「それっぽい数式を出力するが、そもそも変数とパラメタの区別すら曖昧な論理的に読めないもの」が出力されていました。今回のものは、多少ミスはあるものの修正可能であり、最適化モデリングをするうえでの「一歩目」としては利用可能なレベルのものです。 まとめ まとめです。 有名問題をモデル化できるか / 有名定石を利用できるか? ▶ できる 有名問題に簡単な要件を追加してもモデル化がうまくいくか? ▶ 概ねできる 複雑な要件を上手にモデル化できるか? ▶ 間違えはするが、意味のある出力を出せる GPT-5 Thinking は有名問題と有名定石の簡単な組み合わせにとどまらず、 複雑な要件について、多少のミスを含みながらも論理的に考えることができるようです。 MIPモデリングに関して、 GPT-5 Thinking は以下の3つの使い方ができそうです: 数理最適化初心者が簡単なモデルを作りたいとき・学習したいとき 簡単なモデルは作ってくれる ただし正誤判断は自分でできる必要があるため、 教科書で基本的な勉強をしたうえで利用する 中級者~上級者レベルの人のモデリング補助 LLMの知識の幅は圧倒的。調査・検討をしたいときに「ダメもとで聞いてみる」のには意味がありそう たとえば、定石をしらない問題がでてきたときに(枝葉の要件を削って)モデル化させてみて、良さそうなものを使う and/or 出典を調べて調査する、など NEW! 複雑なモデリングの補助 複雑な要件を与えても、検討可能な意味のあるモデルを出力できる 場合によってはモデリングの「一歩目」をLLMに任せて、あとは人間が修正する、という使い方ができそう LLMを使って面白いモデルを書いていきましょう!
はじめに はじめまして。Insight Edgeデザイン部 共創設計チームの小森谷です。 本記事は、私が2025年の夏にInsight Edgeへ参画して最初に取り組んだプロジェクトについてまとめたものです。 チームの一員としてどのようにプロジェクトを進め、何を感じ、どんな学びを得たのか――そのリアルな過程をお伝えできればと思います。 はじめに この記事でわかること 参画初日のオリエン、最初の仕事は「全社会議」の設計だった 「10日後の全社会議で、Insight Edge Vision2030をテーマにワークショップをしたい」 制約条件とゴールイメージ から“設計の手がかり”を探る 今日は7月15日。開催日は7月25日。本番まで、あと10日...? 素案と対話 ― 背景にある意図を探る 限られた時間の中で最善を尽くすために、とにかく早い段階でドラフトを出すことを意識した。 ワークショップの手法選定と具体化 人間と人間の共創プロセス 人間とAIの共創プロセス 当日の様子 ― “聴き合う”が立ち上がる瞬間 参加者の声(抜粋) これからも「共創」を続けるために 筆者について この記事でわかること 10日で全社会議を設計した“段取り” フィッシュボウルを選んだ背景や経緯と内円の緊張を下げる工夫 AIを「観察者/構造化支援」として扱うワークショップ設計の実践プロセス 参画初日のオリエン、最初の仕事は「全社会議」の設計だった 「10日後の全社会議で、Insight Edge Vision2030をテーマにワークショップをしたい」 笑顔で淡々と語る森さん Insight Edgeに参画した初日、最初のミーティングでそう告げられたとき、私は正直状況をつかみきれていなかった。 けれど、不思議と不安よりもワクワクが勝っていたように思う。 その後のプロセスで、少しずつ Insight Edgeの“文化のようなもの” を体感していった。 配属先は「デザイン部 共創設計チーム」。“共創をデザインする”という言葉の意味を、現場で手探りしながら学んでいった。 本稿では、その10日間を当時の視点と現在の理解の両方から振り返り、初仕事として取り組んだ「Vision2030 ワークショップ」の設計から実践に至る過程を記録したい。 制約条件とゴールイメージ から“設計の手がかり”を探る 今日は7月15日。開催日は7月25日。本番まで、あと10日...? 初日のオリエンで共有されたのは、こんな概要だった。 実質的な準備期間は7営業日 会場は大手町オフィスの会議室 スクリーンは2つ 参加者はおよそ60名 2時間半という限られた時間の中で、前半はマネジメント層によるインプットセッション、後半は参加者同士によるワークショップを行う。 淡々と「Vision2030」について語るCINOの森さんと、静かに頷く共創チームリーダーの飯伏さんを眺めながら、後7営業日とは思えない落ち着きぶりだな、と考えていた。 静かに頷く飯伏さん まだ社内の人の顔もほとんど知らず、物事の進め方もつかめていなかった私は、 会議の設計の前にまず背景や状況の把握に集中 することにした。 2回目のミーティングでは、初回で聞いた内容をヒアリングシートにまとめ、まだ聴けていない部分(空白のセル)を埋めながら、森さんと認識をあわせていく。 (※ヒアリングシート=設計意図や制約を整理するための事前質問票) どんな状態を目指しているのか 参加者にどんな体験をしてほしいか 起きてほしいこと、起きてほしくないこと 一つひとつ確認しながら意図を言語化して、全体像を可視化する。私はこの “解像度を上げていく”時間 がとても好きだ。 ふとした瞬間に出てくる言葉や迷いの中に、設計のヒントが潜んでいる。 この時の森さんは、マネジメントからのメッセージを届けるだけでなく、 メンバー自身が自分の言葉で未来を語れるようになってほしい のだと語っていた。 ひととおり埋めたヒアリングシートを出発点として進行を整理した上で、素案に着手する。 ほとんど同時進行のWBS 素案と対話 ― 背景にある意図を探る 限られた時間の中で最善を尽くすために、 とにかく早い段階でドラフトを出すこと を意識した。 まず手始めに、前半と後半を有機的に接続することを意図して 「アウトプット前提でインプットを聴く」構成 を提案。インプットセッションの冒頭で「本日の問い」を提示した上で、参加者がワークシートにメモをとりながらセッション内容を聴き、後半はそのメモをもとに語り合う仕掛けである。 インプットセッションの流れ アウトプットセッションの流れ ①はマスト、②③は時間があれば、の優先度をつけた できるだけ楽しい雰囲気にするために「旅程表」のフォーマットをアレンジしてワークシートを作成した 上記の他に、コンセプト案、タイムライン、大枠の流れをまとめたスライドなど、当日の流れをイメージできる程度の素案を共有すると、すぐにフィードバックが返ってきた。 「マネジメントのメンバーは会話に混ざるべきか?混ざると誘導っぽくならないか?」 「全員がフラットに話すための仕掛けを入れられないか?」 「そもそも全員が同じように話すのが本当にいいのだろうか?」 「もう少し後半のシャッフルを増やせないか?」 「シャッフルするなら、ワールドカフェとか?」 「いや、今の構成だと時間が足りない」 さまざまな意見が上がると、小さな混沌が生まれる。複数の混沌が混ざって大きな混沌になる。私はそれらにまみれながら、それぞれの意図を探っていった。 ワークショップの手法選定と具体化 人間と人間の共創プロセス 森さんと飯伏さんがどうも「シャッフル」にこだわる様子だったので、あらためて問いを立ててみた。 「シャッフルすることで、何が起きて欲しいのか?」 これを起点に話すうちに、森さんがみている”景色”がよりクリアになっていった。 「部門横断で話す場が少ない、部門によって見えている現実が違っている」 そして 「他の部門の話を聴いてほしいんだよね」 と続けた森さんの言葉をきっかけに、ワークショップの軸は、“語り合う”から“聴き合う”へシフトした。 「聴く」という行為をどう設計するか。 この問いから”フィッシュボウル”という手法に辿り着くまでに、さほど時間はかからなかった。 「フィッシュボウル」とは「金魚鉢」を模した​アクティビティの一種で、​対話を深めながら、その内容を参加者全員で共有することができる。​立場の異なる参加者が、お互いの観点をよく理解し、傾聴することを促進する効果がある。​円座の「内側」で進行する話を「外側」から眺めるという構図から、フィッシュボウル(金魚鉢)と呼ばれている。 素案を起点にしたこの対話のプロセスは、まさに「共創」だったように思う。 人と人とのやり取りの中で「意図」が立ち上がり、少しずつ形を変えていく。 ちなみにこの時点ですでに、全体会議当日の3日前である。 人間とAIの共創プロセス ざっくりの方向性やアイデアが出揃ったところで、AIの出番である。ここからは、実際にAIと共創したプロセスの一部を紹介する。 私はまず素材を一括投入し 「制約(優先事項・時間・人数など)を踏まえて、目的整合性と運営難易度の観点から素案を比較し論点を構造化 してください。」と依頼した。 もちろん一発で整理はできないが、 「なんとなくこれじゃないかな」という直感的な発想が、AIとの協働により構造整理されていく のはとても気持ちがいい。 私にとってAIは「観察者」であり「構造化の支援者」でもある。そしてその“観察”の視点は、人間が見落としがちな設計の盲点を照らしてくれる。 ChatGPTとともに整理した内容の抜粋 AIには3案(ラウンドテーブル・ワールドカフェ・フィッシュボウル)をフラットにインプットしたが、推奨はフィッシュボウル。 「内円の発言者が緊張する」懸念については、ワークシートのメモを元に話してもらうことで多少は緩和するかもしれない。メモは緊張した時の”お守り”にもなる。比較検討の結果、時間効率と参加密度の観点からフィッシュボウルを採用した。 しかし、AIが助けてくれるのは思考プロセスまで。 意思決定の後に待っていたのは最も大きな「山場」、人間の仕事だった。 チーム編成や会議室の配置検討などは森さんと飯伏さんがリードし、ワークシートの印刷や当日の備品の準備は共創設計チーム兼務の楠さんや酒井さんがサポートしてくれた。 そしてマネジメントの皆さんがインプットセッションの資料を準備し、当日の音響や会場設営は経営管理部の皆さんが整えてくれた。 縦横無尽にパスを回しあいながら場を作っていく感覚だった。 会議室におけるフィッシュボウルの配置図:テーブル移動は全員での協働作業となった 当日の様子 ― “聴き合う”が立ち上がる瞬間 そうして迎えた全社会議の当日。 会場セッティングと並行して、ファシリテーターと書記を担う皆さんに「進行ガイド」の説明をしたのだが、なんとも不思議な安心感があった。初めまして、とは思えない温かくサポーティブな雰囲気に、ああ今日はきっとうまくいくな、と思った。 開始30分前の雰囲気はこんな感じ 前半のインプットセッションでは、マネジメント層から「Vision2030」の背景と想いが語られた。 会場は静かだったが、ただの“聴講”の静けさではない。 ワークシートにメモを取りながら、自分の中の“問い”を探しているような集中力 が、会場に満ちていた。 Vision2030の背景や意図を聴く 後半のフィッシュボウルが始まる。 進め方を説明した後、森さんと飯伏さんから参加者のみなさんへのメッセージをもらった。 森さん「ぜひこの機会に、他の部門の考えていることを聴いてください。」 飯伏さん「金魚鉢の外側で聴く時にもぜひメモをとってみてください。」 3つの金魚鉢の中心の円で数名が対話し、周囲の人たちはそれを静かに聴く。 4回のターンで円の内と外が入れ替わり、発話が循環していく。 この「話す」と「聴く」が混ざり合いながらひとつの景色を描いていく構造は、今回の全社会議で目指した“共創”のあり方そのものだった。 ファシリテーターが全体の流れを見守りながら、発話のテンポや入れ替わりのタイミングを調整していく。 書記はリアルタイムでMiroに要点をまとめ、話の展開を可視化していく。 このサイズのフィッシュボウルを3つ同時進行した 私はその全体を眺めながら、この“聴き合う”という構造が確かに機能していることを感じていた。 中心の会話が外側に波及し、静かに聴いている人たちの表情や姿勢にも変化が生まれていく。時おり聞こえてくる朗らかな笑い声が印象的だった。 組織ごとを自分ごととして捉えるには、自分自身の言葉で“語る”ことが重要だ。 けれど、“聴き合う”という行為を構造として仕込むことで、全員がその対話の一部になる。それが、この日の場で起きていたことだった。 参加者の声(抜粋) フィッシュボウル形式は非常に好評で「新しいスタイルとして参考にしたい」「観察も発言も楽しめた」との声が多数。 他部署・他職種の考えに触れることを「刺激的」「意外だった」と評価する傾向が強く、相互理解の促進効果が確認できた。 一方で、「書記の負担が大きい」など運営面の改善要望が複数寄せられた。 これからも「共創」を続けるために 全社会議を終えてしばらく経ったころ、社内のさまざまな場で「フィッシュボウル」という言葉を耳にするようになった。噂によると、別の部門のイベントでも実施されたらしい。 一度きりの企画で終わらず、“聴き合う”という構造が、組織の中に息づいている。 それは今回の設計の成果のひとつだと思う。 このプロジェクトを通じて、共創の場づくりは 「正解を示すこと」ではなく、「問いを共有すること」 なのだと実感した。 同じテーマを前にしても、立場や経験によって見えている景色は違う。 その違いを前提に「共通の問い」に向き合っていく ことが、共創の出発点になる。 短期間の中で進めた今回の設計には、もちろん反省も多い。 アンケート結果を眺めながら、今後の糧となる示唆をたくさん受け取った。 Insight Edgeの「共創設計チーム」は、そうした試行を通じて実践知を積み重ねていく組織 なのだと思う。 これからも、ステークホルダーを広く捉えながら「共創の価値」について考え続けていきたい。 筆者について 小森谷 有紀(こもりや ゆき) 紙媒体の編集者を経て、ポータルサイトの速報エディターに転身。​通信キャリア直下のモバイルメディア事業において、黎明期の立ち上げから拡大、統合まで幅広い業務を経験した後、​​複数のスタートアップ企業にてコロナ禍のリモート環境における事業企画や組織開発の仕組みづくりを推進。​​2025年7月よりInsight Edgeに参画。デザイン部・共創設計チームに所属し、ワークショップデザインやファシリテーションを担当。
はじめまして。Insight EdgeにUI/UXデザイナーとして参画している、アマガスと申します。 今回、Insight Edge(以下、IE )のブログを執筆するにあたり、DX化推進支援・生成AI活用の現場へUI/UXデザイナーとして参画している意義や、そこで得られた経験について綴ってみました。 なぜDX化推進支援の領域にデザイナーが必要なのか Insight Edgeにおけるデザイナーの役割 1. 体験の価値を見出し、戦略につなげる「UX」 2. 誰もが迷わず使える体験を形にする「UI」 3. 複雑な情報を直感的に伝え、その価値を高める「グラフィックデザイン」 4.制作実績の紹介 体験をデザインし、価値ある形にする、体験設計チームの一気通貫なものづくり Insight Edgeデザイン部のチーム構成 自分の手で形にする面白さ 現場で得る学び 発想の幅の広がり “やりぬく、やってみる、みんなでやる” ─ 私が感じたIEの魅力 まとめ なぜDX化推進支援の領域にデザイナーが必要なのか 顧客のDX化推進支援において、生成AIのように新しい仕組みを取り入れるプロジェクトでは、仕組みがまだ未知な部分も多く、最初はなかなかピンとこなかったり、興味を感じにくかったりすることもあるのではないでしょうか。 その過程で情報の本質を整理し「どうすれば伝わりやすいか」を考え、目指す形を導き出すことこそが「デザイン」の重要な役割となってきます。 デザインは「見た目を美しく整える」だけでなく、「理解して形にする」ことが役割に含まれているので、DXや生成AIのような目新しい領域では「体験」を形にするUI/UXデザイナーの存在が欠かせないと思います。 Insight Edgeにおけるデザイナーの役割 IEのデザイナーは、多岐にわたる領域に携わり、その線引きは曖昧です。それでも、一人ひとりが状況に応じて柔軟に動きながら、確かな価値を生み出しています。
ここでは、実際の役割や制作事例をご紹介します。 1. 体験の価値を見出し、戦略につなげる「UX」 顧客に向けたワークショップを行い、DX化に向けた認識を深めたり、アイデアを共創することから始まることもあります。 また、マーケティングの視点を取り入れたり、エンジニアの技術的思考とビジネスサイドの視点、そしてユーザーやクライアントの体験をつなぐ役割も担います。 プロジェクトの初期段階では、ヒアリングやユーザーリサーチを通して現状(As-Is)を把握し、理想の体験(To-Be)を描きながら要件を整理します。 そのうえで、カスタマージャーニーや情報設計などを用いて「どんな体験を、どんな流れでユーザーへ届けるか」を明確にし、関係者と共有します。 ときにはコンサルタントのように、相談段階から課題を整理し、デザインを通して体験のゴールや、方向性を共に考え提案することもあります。 IEでは案件によっては開発が先に進み、後からデザインが加わることもあります。その際に「目指す形」を見直し、体験全体をブラッシュアップできるのは大きな価値だと思います。 2. 誰もが迷わず使える体験を形にする「UI」 SaaSをはじめとしたプロダクト開発では、直感的に使えるUI設計が欠かせません。 使いづらいと感じた瞬間に、ユーザーは離れてしまうからです。使いやすさは安心感と信頼感を生み出す大切な要素です。 デザイナーはユーザー目線で操作フローを検討し「誰でも迷わず使え、ストレスのないUI」をビジュアルにします。 体験設計チームでは、UXを基に、ユーザーの行動や利用シーンを想定しつつ、操作フローや画面構成をデザイン&検討しながら進めます。 実際にはFigmaを使ってワイヤーフレームやプロトタイプを作り、プロジェクトチーム内やクライアントと合意形成を図りながら「目指す形」を明確にしていきます。 必要に応じてエンジニアやビジネスサイドとも意見を交わし、UIの動きや導線を調整していくことで、「誰でも迷わず使える」体験を実現してゆきます。 3. 複雑な情報を直感的に伝え、その価値を高める「グラフィックデザイン」 DX化や生成AIなどの活用の仕組みは、文章だけではなかなか理解しにくいものです。 その情報をわかりやすく可視化し、共有を促すのがビジュアルデザインの役割です。 複雑な仕組みを図やイラスト・チャートなどに落とし込むことで、ユーザーはもちろん、社内メンバーや経営層にも直感的に伝えることで価値を高めることができます。 実際、イベントの場でも「ポスターやチラシがあったおかげで来場者に説明しやすかった」と言っていただけることもあり、デザインの推進力を実感しています! 4.制作実績の紹介 体験設計チームで携わったデザインの一部(おもにグラフィックデザイン)をご紹介します。 体験をデザインし、価値ある形にする、体験設計チームの一気通貫なものづくり Insight Edgeデザイン部のチーム構成 IEのデザイン部には「共創設計チーム」と「体験設計チーム」があります。 私が参画している「体験設計チーム」では、上流の体験設計を担うサービスデザイナー・UXデザイナー、UIやグラフィックを形にするデザイナー、そしてコンサル的な動きからUX〜UI、撮影まで幅広く関わる忍びの者など、多様な人がいます。 共通しているのは「どうすればわかりやすく、価値ある形で届けられるか」を常に考え続けている点ではないかと思います。 そのためには、エンジニアやビジネスサイドともフラットに会話したり、チーム内でも「もっとこうしたほうがよい」と意見を出しながら進めたりする場面がよくあります。 実際、体験設計チームでは、Figmaの画面を2〜3人で共有し、会話ベースで一気に作業を進めることもあります。 デザイナー同士だと話のテンポが速く、上記画像左のワイヤーフレームは約1時間で形になりました。 出来上がったLPが こちら です。 about.voiceek.com またあるときは、営業資料などを2人で手分けしてさくっと仕上げてしまうこともあります。 お互いの考えや進め方を間近で感じられるのが魅力で、ざっくばらんな雰囲気の中でアイデアがどんどん形になっていく手法が、最近お気に入りの進め方です! 自分の手で形にする面白さ IE内で過去に事例のあるプロジェクトでは、例えばWEBデザインであれば、要件定義からデザイン制作、モックアップからコーディング・実装、ミーティングでのファシリテーションやクライアント対応、スケジュール管理も含め1人で担当することがあります。 ワンオペで進めるには工夫が求められますが、コミュニケーションの中間工程が少ない分、要件定義からデザインへの落とし込みがスピーディーです。 さらに、クライアントの声を直接聞いているため、要望を反映しやすく、より解像度の高いデザインにつなげられる点も大きなメリットです。 そして、完成したデザインを評価いただけたときの喜びはひとしおです。 現場で得る学び プロジェクトの中にはUXが固まる前に開発が進み、後からUIを整理する場面もあります。 時には急な対応が必要となり、爆速で形にすることもありますが「乗り越えられなかったこと」は一度もなく、その時々で新しい学びを得ることができています。 発想の幅の広がり IEではMTGなどで発表の機会が多く、発信力やコミュニケーション力が自然と高められます。 また、生成AIを活用したクリエイティブ環境に触れることで、デザイン表現の幅が広がるとともに、制作にかかる時間を効率化できるようになってきました。 その結果、制作物のクオリティ向上に注力できるようになっていると思います。 “やりぬく、やってみる、みんなでやる” ─ 私が感じたIEの魅力 IEの魅力は「技術者を大切にしてくれる会社」であることだと思います。 成果を見逃さず、良い点をしっかり評価していただけることが、大きな励みになっています。 クライアントへ期待以上の成果を届けているのも、素晴らしいと思います。 内製ならではのスピード感と、精鋭メンバー全員が自分の領域に誇りを持って品質を磨き続けている姿勢、それは「やりぬく」「やってみる」「みんなでやる」というスローガンのとおり、技術者同士が協力し合える環境があるゆえのことだと思います。 まとめ ここまでお読みいただき、ありがとうございました。 Insight EdgeのUI/UXデザイナーの仕事に、少しでも興味を持っていただけたら幸いです。
IEでは、顧客のDX化推進支援や生成AIといった最先端の領域に携わることができます。 デザインだけでなく、戦略や企画の段階からプロジェクトに関わることもあり、その中で幅広いスキルを身につけながら、社会に価値を届ける「体験」をデザインで形にできる環境があります。 現在、IEでは UIUXデザイナーを募集 しています。これまで培ってきたデザイナーの経験を活かし、「伝わるデザイン」を一緒につくりませんか?
本記事でわかること はじめに 背景・課題 目的 GitHub Actionsを用いたSpec Kitで仕様駆動開発を試してみる 仕様駆動開発とは Spec-Kitとは Claude Code GitHub Actionsについて オセロ対戦アプリを作ってみた Issue連携とSub-issueの活用 問題点と所感 テスト駆動開発の無視 まとめ 参考資料 本記事でわかること この記事では、AIエージェント時代の新しい開発手法として注目される「仕様駆動開発」を、 Claude Code GitHub Actions と Spec Kit を使って実際に試した結果をお伝えします。オセロアプリの開発を通じて、従来の開発プロセスとの違いや実際の課題までを解説します。 はじめに こんにちは。 この度Insight Edgeで1ヶ月間のインターンに参画しております、東京科学大学物質理工学院博士課程2年の石井です。大学院では主に半導体材料をターゲットとした第一原理計算を用いた点欠陥の解析などを行なっております。 今回は、インターン期間中に検討したClaude Code GitHub ActionsとSpec Kitを用いた仕様駆動・AIエージェント駆動開発に関する話をご紹介しようと思います。 背景・課題 ソフトウェア開発は、単一の生成AIによる点的な支援から、 複数モデル/エージェントが協調して要件理解・設計・実装・検証を一貫支援する「エージェント駆動型」 への移行過程にあります。主要LLMと周辺ツールの高度化により自律実行や運用レベルのコーディングが可能になり、開発者は本質的な課題解決に集中できる環境が整いつつあります。 一方で、活用はまだ局所最適に留まりがちで、プロジェクト/個人間でのばらつき、ならびにAI活用プロセスの可視化・再現性の不足が課題です。具体的には以下が挙げられます。 情報探索や手戻りが散発的に発生しやすい ドキュメントと実装の同期が人手依存で、保守性・説明可能性に揺らぎがある ローカルでのAI活用は進む一方、成果物管理やレビューの基盤(GitHub等)が従来運用のままで、活用過程がログとして残らない/監査できない 組織横断で再利用できるプロンプト・エージェント設計・ワークフローが標準化されていない 目的 そこで今回は、組織プロセスとして定着させるための手段としてGitHub Actionsに組み込み、GitHub上で実行することを目指します。 これにより、 情報探索や手戻りの削減 ドキュメントと実装が自然に同期され、プロダクトの保守性が高まる AIエージェントを前提とした開発プロセスが可視化される が成し遂げられ、チーム開発における 開発者体験(Developer Experiense)の質的向上 と、 組織ノウハウとしての定着 が期待されます。 今回はAIエージェントとして Claude Code を活用します。 GitHub Actionsを用いたSpec Kitで仕様駆動開発を試してみる 仕様駆動開発とは 仕様駆動開発(Spec-Driven Development; SDD)は、AIが直接コードを生成する方式ではなく、まず仕様書(Specification)を定義し、その仕様を根拠としてAIエージェントが開発を進める手法です。 仕様駆動開発のねらいは、即興的なvibe codingで失われがちな合意や根拠を仕様へ明確化し、変更時も仕様差分を基点として安全かつ高速で反復できる状態を実現することです。 仕様駆動開発の応用例として、Amazonが発表したエージェント型AI統合開発環境(IDE)である Kiro も注目されています。Kiroは、自然言語のプロンプトや要求から明確な仕様を起こし、それをもとに要件分解→設計→実装タスク化→テスト生成→コード生成までを支援するものです。 Spec-Kitとは そして、その仕様駆動開発のワークフローの標準化のためのツールとして注目されているのが Spec Kit です。 Spec Kitは与えた仕様を詳細化して仕様書を作成し、ソフトウェア開発の計画を策定してタスクに分解・実装するためのオープンソースツールキットでGitHubから提供されています。 Claude Code GitHub Actionsについて また、今回はAIエージェントの活用過程がログとして残らない/監査できないという現状へのアプローチとしてGitHub Actions上で仕様駆動開発を試みます。 そのために今回は、Claude Code GitHub Actionsを活用しました。フローとしてはまずIssueを作成しIssueのコメントで @claude をメンションし、続けて本文を投稿するとその内容をプロンプトとして解釈して返信が返ります。その実装をレビューしてPRを出します。 こちらの 公式サイトのworkflowファイル作成 を参照し、 github/workflows/ 配下へymlファイルを追加しました。 オセロ対戦アプリを作ってみた Claude Code GitHub ActionsとSpec Kitを用いた仕様駆動開発のデモンストレーションとして、オセロ対戦アプリを作成しました。 spec-kitの代表コマンドとして以下のようなものがあり、これらを順に実行していくことで仕様駆動開発が実現できます。 /specify コマンドから要求を書き仕様書を生成 /clarify コマンドで作成した仕様書の未定義箇所の穴埋め /plan コマンドから対応してほしい技術スタックを書き込んで詳細設計書を生成 /tasks コマンドを叩くとtask.mdファイルとしてTodoリストが作成 /implement コマンドでtask.mdを読み込んでそのタスクを元に実装 これらのコマンドはすべてSpec Kitの初期化時に作成されます。 まずはuvを事前にインストールし、以下を実行します。 uvx --from git+https://github.com/github/spec-kit.git specify init --here Claude Codeではなく、Geminiでspeckitを使用したい場合は uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai gemini のように --ai <使用したいcoding agent> で使用できます。(geminiの場合は.gemini/commands/にtomlファイルが生成されます。)対応しているagentの種類はリポジトリを参照: Spec Kit そして以下の画像のように、Issueを立ち上げて @claude でClaudeをメンションして雑な要件定義を投げます。 @claude /specify モダンなUIのオセロ対戦アプリを開発してください。CPUとの対戦が可能なアプリとし、CPUとの対戦は「弱い」、「普通」、「強い」の三種類を選べます。 specifyコマンドの実行例 そうすると、上記の要件で未定義で曖昧な箇所を以下のようにリストアップしてくれます。 未定義な項目のリストアップ例 上記の未定義な箇所の埋め合わせは /clarify コマンドで以下のようにPR上でコメントします: @claude /clarify 1. プレイヤー対プレイヤーのモード: CPU対戦のみ 2. ゲーム履歴の保存: 過去のゲーム結果や統計情報の保存は必要なし 3. サウンドとアニメーション: 駒を置く音、裏返すアニメーション等は必要なし 4. プラットフォーム対応: Webにのみ対応 5. CPU思考時間: 即座に応答すべきか。ただしCPUの処理レベルが高い場合、視覚的な表示や遅延処理が必要 6. ヘルプ機能: 有効な手のヒント表示やアンドゥ機能は必要 これにより曖昧な未定義箇所を穴埋めした仕様書(spec.md)が完成します。 そして、次に実装計画の作成するために /plan コマンドを実行して技術要件の定義や設計、実装手順の策定します。 @claude /plan これを React + TypeScript で実装し、ビルドは Vite、状態管理に Zustand を使用します。スタイリングは Tailwind CSS と shadcn/ui(Radix UIベース)でモダンかつアクセシブルなUIを構築します。盤面描画は解像度に依存しない SVG を基本とし、将来的なアニメ最適化に備えて Canvas 実装も抽象化レイヤーで切り替え可能にします。 CPU思考はUIスレッドをブロックしないよう Web Worker に分離し、アルゴリズムは Minimax + Alpha-Beta を採用(弱=深さ2+簡易ヒューリスティック、普=深さ4、強=反復深化6–8/時間上限10秒)。ハッシュは Zobrist、思考中インジケータとUX用の意図的ディレイはUI側で制御します。 アクセシビリティは ARIA と react-aria に準拠し、レスポンシブ対応・コントラスト基準を満たします。国際化は react-i18next で実装し、チュートリアルは MDX で管理します。 永続化要件は最小のためバックエンドやDBは不要(サーバーレス/静的ホスティング: Vercel または Cloudflare Pages)。任意で設定のみ LocalStorage に保存します。 プロジェクト構成は core/(盤・合法手・反転・評価・探索の純関数)と ui/(表示・操作)、workers/(AI思考)に分離し、仕様の FR 群(合法手表示、無効手の視覚フィードバック、終了判定、難易度選択、思考インジケータ、終了ダイアログ、終了後の新規対局)を満たすタスクに分解して実装します。 そうすることで、技術スタック定義やプロジェクト構造などが書かれた計画文書(plan.md)、技術領域について詳細調査と選定理由を文書化した技術調査文書(research.md)、などを生成してくれます。 次に、plan.mdで定めた実装手順をベースに、それをタスクに落とし込む /tasks コマンドを実行します。その結果、一連の全ての実装タスクをtasks.mdに作成してくれます。 @claude /tasks 最後に /implement コマンドで実装します。以下のように「並列で実装できるタスクは並列で実装して」のようなプロンプトを追加で入れると、tasks.mdのタスクの中で並列に実装可能と判断したタスクを並列で実装してくれるようになります。 @claude /implement 並列で実装できるタスクは並列で実装して。 全ての実装が完了後、軽微な修正であればそのままClaudeにメンションして修正させることができます。大きな追加実装であれば新たに /specify コマンドで追加機能の要件を定義して上記のフローを実行します。 以下は実際に出来たオセロ対戦アプリの画面の一部です。 作成したオセロの画面 Issue連携とSub-issueの活用 GitHubを活用した開発では、Issueに書かれた要求を起点として開発するIssue駆動開発(チケット駆動開発)が一般的です。 そこで今回は、Issueを起点に作業を計画し、必要に応じてsub-issue機能も活用するため、 /create-sub-issue コマンドを新たに作成しました。このコマンドは親issueを細分化してsub-issueを作成する機能を持ち、実際は以下のフローになります。 親Issueを立てる 親Issueのスレッドでまずは大まかな作業計画を立てて、親Issueに紐づくブランチ(親ブランチ)でspec.mdを作成し、mainブランチターゲットでPRを作成(親PR) 親PR上でspec.mdのレビューをし、親PR上でplan.md、tasks.mdを続けて作成 /create-sub-issue コマンドでtasks.mdの内容を参照し、sub-issue作成の指示を出してsub-issueを作成 sub-issueの作業内容は、親ブランチを起点として作成し、PR作成時にも親ブランチをtargetに設定 作成されるsub-issueには以下のルールが適用されます: タイトルに親issue番号と連番を付与 最大6個まで作成可能 Github MCPの機能でissueの作成と親子関係を自動化 コマンドの内容は以下(create-sub-issue.md): --- description: 親Issueを細分化してsub-issueを作成するコマンド。以下のsub-issueの作成ルールに従ってsub-issueを作成してください。 --- #### **タスクの進め方** 元の大きなタスク(親Issue)を、いくつかの小さな作業(sub-issue)に分けて進めていきましょう。 ### **1. タスクの分割** まず、元のタスク(親Issue)の内容をよく読んで理解し、それを具体的な作業に分解します。分解した作業は、新しいタスク(sub-issue)として登録してください。 * **作成方法**: Github MCPの ` sub_issue_write ` という機能を使ってsub-issueを作成してください。 * **個数**: 作成するsub-issueは**6個まで**にしましょう。 ### **2. sub-issueのタイトルルール** 作成するsub-issueのタイトルは、以下のルールに従ってください。 * **フォーマット**: タイトルの先頭に ` [親Issue番号-連番] ` を付けます。 * **例**: 親Issueの番号が「123」で、最初の作業なら ` [123-1] 〇〇の実装 ` のようになります。 * **連番**: 連番は、作業を進める順番(優先度が高い順)に ` 1 ` から振ってください。 ### **3. タスクの親子関係を設定** Github MCPの ` sub_issue_write ` という機能を使って、元の親Issueと、新しく作成したsub-issueを関連付けます。これにより、タスクの全体像が分かりやすくなります。 ### **4. 作業用ブランチの準備** 最後に、**親Issue用のブランチ**を1つ作成し、変更が何もない状態で構わないので、一度プッシュしてください。 ##### **なぜこれが必要?** この親ブランチを基準点(出発点)として、各sub-issueの作業用ブランチを作成したり、作業完了後のプルリクエストの送り先に指定したりするために必要です。 また、デフォルトのClaude Code GitHub Actionsではsub-issueを作成する権限がないため、親Issueに紐付けてsub-issueを作成するにはGitHub MCPを使用しました。 .github/workflows/ 内のymlファイルのClaude Code GitHub Actionsの設定にclaude_argsとして以下のMCP周りの権限を追記します。 claude_args: | --mcp-config .github/mcp-servers.json --allowedTools \ Bash, Write, Edit, MultiEdit, Read, LS, Glob, Grep, WebFetch\ mcp__github__create_branch,\ mcp__github__issue_read,\ mcp__github__issue_write,\ mcp__github__sub_issue_write,\ mcp__github__list_sub_issues,\ mcp__github__create_pull_request,\ mcp__github__get_pull_request,\ mcp__github__get_pull_request_diff,\ mcp__github__pull_request_read,\ mcp__github__update_pull_request,\ mcp__github__create_pending_pull_request_review,\ mcp__github__add_comment_to_pending_review,\ mcp__github__submit_pending_pull_request_review また上記に含まれる .github/mcp-servers.json の設定は以下のようにしました。 { " mcpServers ": { " github ": { " command ": " docker ", " args ": [ " run ", " -i ", " --rm ", " -e ", " GITHUB_PERSONAL_ACCESS_TOKEN ", " ghcr.io/github/github-mcp-server " ] , " env ": { " GITHUB_PERSONAL_ACCESS_TOKEN ": " ${GH_TOKEN} " } } } } これにより、tasks.md作成後に @claude /create-sub-issue とコメントすることで、親Issueに紐付いたsub-issueが作成されます。実装はそれぞれのsub-issue内で実装コマンドをコメントしてClaudeに実装してもらいます。 こうすることで、Issue駆動な仕様駆動開発も実現可能です。 作成されたsub-issueの例 問題点と所感 仕様駆動開発を試行錯誤しながら試していく中で、以下のような課題点やトラブルシューティングに直面しましたので一例として紹介します。 テスト駆動開発の無視 Spec Kitではテスト駆動開発(TDD)を標準としています。 しかし、オセロ対戦アプリの実装では、下図のとおりテストコードが作成されないケースもありました。 原因は、 /implement コマンドでtasks.mdのタスクを実装する際に、テスト実装の優先度が低く設定されているためと考えられます。 TDDがスキップされた例 これを解決するための暫定アプローチとして、CLAUDE.mdに以下のような記述を追加することで、テスト駆動開発を厳守させるようにしました。 CLAUDE.md: # 原則 - すべての変更は TDD(Red→Green→Refactor) を厳守する。 1. Red: テスト作成 → 失敗を確認 → テストのみコミット 2. Green: 実装作成 → すべてのテストを通す → 実装のみコミット(テストは変更禁止) 3. Refactor: ふるまい不変の範囲でリファクタ → 実装のみコミット(テストは原則変更禁止) - テストの改変は原則禁止。ただし、誤仕様または明白な不具合の場合に限り、tests-fix サブタスクとして別PRで扱う。 - モック実装・ハードコードは禁止(テスト過適合を避ける)。必要なら最小限のテストダブルを使用し、ケース拡張で過適合を検出。 - sub-issueのタスクとしてファイルを作成・編集する場合、mainブランチではなく、親Issueのブランチから分岐してブランチを作成してください。 - PRを作成する場合も、mainブランチではなく、親Issueのブランチに対して作成してください。 - 仕様の明確化のために質問・確認事項はまとめて質問してください。断片的に質問を繰り返すことは避けてください。 - 全ての会話の応答や文章の作成には日本語で出力してください。 - markdownファイルへの記述も基本的に日本語で行なってください。 自分は上記のCLAUDE.mdを編集することにより、TDDの強制を改めて行いました。一方でSpec Kitのコマンドを活用するのであれば、 /constitution <instruction> コマンドを実行して開発ツールや開発スタイル、コーディング規約などを指定することによりTDDを指定するのも良いかもしれません。またチーム開発の際は、チーム規約の追加も /constitution コマンドで指定しておくとSpek Kitのコマンドをさらに活用できると考えられます。 1 まとめ 本記事では、Claude Code GitHub ActionsとSpec Kitを組み合わせることで、仕様駆動・エージェント駆動開発を試してみました。また、Issue/sub-issueとも組み合わせることで、Issue駆動な開発も実現できました。この手法により、ターミナルを開いて自分でコードを書かなくともGitHubのブラウザ上でのコメントによる操作のみで、上記のようなアプリが開発できたのは非常に感慨深かったです。 本記事で扱ったような内容が、現場に寄り添った提供方法の一案として検討していければ幸いです。 参考資料 SpecKitでどこまでできる?コストはどれくらい? https://speakerdeck.com/leveragestech/speckitdedokomadedekiru-kosutohadorekurai ↩
はじめに こんにちは!開発チームで生成AI関連のシステム開発をしている広松です! 今回は私が担当した案件で発生した「自律型AIエージェントが複雑な指示を途中で忘れてタスクを完遂できない」という課題に対して論文を元に対策を調査してみました。 具体的にはマルチエージェント化やオーケストレーターの導入によるコンテキストエンジニアリングについて論文を中心に調査してみました。 はじめに 案件で発生した課題 課題と原因について 解決策(コンテキストエンジニアリング)について 手法 1. Plan and Act 2. 階層型マルチエージェント(オーケストレーター) 3. 特化型の専門家エージェントへの分解 まとめ 参考文献 案件で発生した課題 私が担当した案件で「自律型AIエージェントが複雑な指示を途中で忘れてタスクを完遂できない」という問題が発生しました。以下では、この課題の具体的な状況を説明します。 案件自体は自律型AIエージェントでIT運用を自動化するものでした。IT運用で広く横展開できるAIエージェントが実現できるのか検証することが目的であったため、今回はワークフロー型のエージェントではなくReAct型の自律的に思考するエージェントで検証をしました。 実装したエージェントはReAct型のシングルエージェントで6つの自作MCPサーバーと接続し計12ツールを操作するものでした。各種運用対象のサービスがMCP未対応だったため、一時的に自作MCPを用いてツールを接続しました。 エージェントへの指示内容は以下のような流れでした。 各種サービスを操作 例外が発生した場合は原因と解決策を調査 解決策を提示し、人間オペレーターの承認を取得 問題を解決し、最初の指示を完遂 しかし、例外対応時にエラー解決した時点でタスクを完了したと誤認し終了してしまう確率が高かったです。およそ50%程度途中終了する事態が発生していました。 このため、「自律型AIエージェントが複雑な指示を途中で忘れてタスクを完遂できない」という課題に対しての解決策を探す必要が出てきました。 課題と原因について 先ほどの案件で発生した課題を一般化して解くべき問題を特定して論文で調査したいと思います。 案件で発生していることは一般化すると以下のようになります。 自律型AIエージェントは自ら思考、計画、実行、結果の観察を繰り返すことで目的を達成する仕組みです。しかし、複雑な指示を与えると途中で本来の指示を忘却してしまい、途中で終了したり指示を無視した行動を取ることがあります。 この問題は、多くの論文で指摘されていて、様々な解決策の提案(後述で紹介)や エージェントの性能を測るためのベンチマーク に多段階の複雑なタスクを完遂できるかという項目が含まれていたりします。 多くの論文でこの課題の原因については、コンテキストが多くなりすぎて一貫性を保てなくなるからとされています。 エージェントが高レベルの計画を立てながらと低レベルのツール操作と環境からのフィードバックを1つのコンテキスト内で管理していくうちに一貫性が保てなくなり破綻していくことが原因と指摘されています。 解決するためにはコンテキストをどう管理するか、つまり一般的に「コンテキストエンジニアリング」と呼ばれている手法を試す必要があると考えられます。コンテキストエンジニアリングの中で、マルチエージェント化や関心の分離など様々な手法があるため調査します。 解決策(コンテキストエンジニアリング)について 先ほど、課題の原因はコンテキストが多くなりすぎて一貫性を保てなくなるからと確認しました。 解決策としてはコンテキストエンジニアリングと呼ばれるコンテキスト管理の方法が有効のようです。 そもそもコンテキストエンジニアリングについてご存知ない方もいると思うので簡単に説明します。 LangChainのブログ にもまとめられている通りエージェント向けのコンテキストエンジニアリングには下の4つの手法があります。 コンテキストエンジニアリングの4つの手法 Write Context:セッション内のやり取りをメモリ機能などで外部に保存することでコンテキストの肥大化を防ぐ方法です。 Select Context:外部ツールなどから必要なコンテキストだけを抽出する仕組みです。これによってコンテキストの肥大化を防ぎます。 Compress Context:コンテキストが多くなるとコンテキストの圧縮で要約や切り取りをします。 Isolate Context:コンテキストを独立させることです。マルチエージェント化などを実施し関心の分離をして、コンテキストもそれぞれのエージェント内で独立させることにより同一コンテキストの肥大化を防ぎます。 この4つの手法の中で今回有効なのはIsolate Contextと判断しました。理由は今回複数のツールを扱い環境からのフィードバックを得て試行錯誤する過程で、当初の指示を忘却して直近のタスクだけ終わらせて終了してしまうためです。 そこで、記載されている通りマルチエージェント化、例えば作業するエージェントと計画を立てて全体をコントロールするエージェントを分けて関心の分離によるコンテキストの分離を実施すれば有効に機能するのでは?と仮説を立てました。 手法 エージェントによるIsolate Contextというコンテキストの分離にはいくつか手法があるため、それぞれ論文を中心に解説します。 1. Plan and Act Plan and Act と呼ばれる手法について紹介します。 これは高レベルの計画を立てるPlannerと実際にツールを使って操作するExecutorを別のエージェントにすることで関心の分離とコンテキストの分離をする手法です。従来のシングルエージェントでは高レベルの計画の思考とツールを具体どう使うかの思考を同じコンテキスト・同じエージェント内で実施する必要があり複雑なタスクでは高レベルの計画の一貫性を保てなくなることが課題でした。 これを下図の通りPlannerとExecutorエージェントに分け、それぞれ関心とコンテキストを分離して課題を解決しています。 Plan and Actの概念図 Plan and Actの流れ この手法によりWebArena‑Lite57.58%、WebVoyager81.36%とそれぞれのベンチマークでSOTAを達成しているようです。 PlannerとExecutorを分離することで例外発生時や複雑で長期なタスクであっても、Plannerが高レベルの計画についてのみコンテキストを保持することで最初の指示を忘れることなく、指示を完遂する確率を上げることができます。 2. 階層型マルチエージェント(オーケストレーター) マルチエージェント化でよく使われる手法としてはオーケストレーターとサブエージェントに分ける方法です。 これは先ほどのPlan and Actの発展系でもあり、オーケストレーターは計画を実施してメインの指示をサブタスクに分解し、サブタスクをサブエージェントで実行させる手法になります。 こちらは論文ではないのですが、Anthropicのブログ How we built our multi-agent research system が非常にわかりやすく解説しているので紹介します。 Claudeのオーケストレーターアーキテクチャー Claudeのオーケストレーターの処理フロー 図の通りLeadResearcherエージェントがオーケストレーターとなって指示をサブタスクに分解し、サブエージェントにアサインしています。 この際、オーケストレーターによるサブタスクの言語化が明確にできていないと、複数のサブエージェントは重複したタスクを実行してしまいます。重複や非効率の問題があるためオーケストレーターによるサブタスクの言語化は慎重にチューニングが必要だそうです。 しかし、オーケストレーターが的確にサブタスクを生成してサブエージェント用のプロンプトを生成できるようになると複雑な調査を計画立てて効率よく行うことが可能になったと記載されています。 3. 特化型の専門家エージェントへの分解 最後に特化型の専門家エージェントに分解する手法について説明します。 先ほど紹介したオーケストレーターの事例では汎用的なサブエージェントに都度サブタスクのプロンプトを渡してタスクを振っていました。 しかし、扱うツールが多くなるとエージェントがどのツールを使ってどのようにタスクを進めるかまで毎回決定させる必要があります。この時点でコンテキストを多く消費したりうまくタスクを遂行できない可能性が上がります。 そこでロール単位での専門家のエージェントを事前に定義しておくことで、専門家らしく決まったツールを使ってどうタスクを進めるかを事前定義したエージェントを使用することでこの課題を解決します。 例えば、 HyperAgent: Generalist Software Engineering Agents to Solve Coding Tasks at Scale のようなコーディンマルチエージェントでは下図の通り明確な専門家エージェントへ分けています。 HyperAgentでの専門家エージェントの振る舞い まとめ 今回は実案件で発生した課題「自律型AIエージェントが複雑な指示を途中で忘れてタスクを完遂できない」に対して有効と思われるコンテキストエンジニアリング手法を調査してみました。 具体的な手法を論文を中心に以下3つ紹介しました。 Plan and Act 階層型マルチエージェント(オーケストレーター) 特化型の専門家エージェントへの分解 手法1~3の順に実装難易度が上がっていくと思うので、実装コストと精度を鑑みて皆さんも複雑なタスクを完遂するエージェントシステムを構築してみてください! 参考文献 Liu, X. et al. (2023) . AgentBench: Evaluating LLMs as Agents. arXiv preprint arXiv:2308.03688. Erdogan, L. E. et al. (2025) . Plan-and-Act: Improving Planning of Agents for Long-Horizon Tasks. In Proceedings of the 42nd International Conference on Machine Learning (Vol. 267, pp. 15419-15462) . PMLR. Phan, H. N. et al. (2024) . HyperAgent: Generalist Software Engineering Agents to Solve Coding Tasks at Scale. arXiv preprint arXiv:2409.16299.
導入 初めまして。Insight Edgeで企業のDX・AI活用をご支援しているセールスコンサルタントです。 これまで様々な大企業の全社横断的なプロジェクトに携わってきましたが、 DXがうまくいかない企業に共通する、いくつかの「つまずきの要素」があることに気づきました。 「外部の経験豊富なベンダーに頼んだのだから、うまくやってくれるだろう」 そう考えてDXをスタートされるかもしれません。 優秀なコンサルタントやベンダーを雇えば、DXは成功するのでしょうか? 答えは「No」です。 私たちの役割は、あくまで皆さんの挑戦を「支援」すること。主役は、あくまで皆さん自身です。 決して外部ベンダーへの「丸投げ」では実現できません。 特に大規模なDXプロジェクトでは、経営層の号令で始まったものの、現場のリアルな課題とズレてしまったり、 推進担当者でさえ「何のためにやっているんだっけ?」と目的を見失ってしまったりするケースが後を絶ちません。これでは、まさに目的地を見失ったまま航海を続ける船と同じです。クライアント側がこうした状態では、私たちがどれだけ伴走しようとしても、やがてプロジェクトは推進力を失い、崩壊してしまいます。 真のDX成功に必要なのは、クライアントとベンダーの強固な「二人三脚」。互いに手を取り合い、同じゴールを目指すパートナーシップが不可欠なのです。 そこでこの記事では、私が現場で見てきたDX推進を阻む「4つの壁」の正体をご紹介します。あくまでその課題は氷山の一角ではありますがこの記事が、皆さんの会社のDXプロジェクトを成功に導くための、確かな一歩となれば嬉しいです。 導入 その「AI導入」、本当に進んでいますか? 第1章:生成AI推進を阻む「4つの壁」とその正体 1-1. 羅針盤なき航海:「とりあえずDX」の罠 1-2. 構造的ボトルネック:DXを阻む旧態依然の組織体制 1-3. 人材の枯渇:誰もいない「推進リーダー」の席 1-4. イノベーションの足枷:変化を拒む社内ルール 第2章:解決の鍵は「アジャイル・ガバナンス」という新しい考え方 第3章:処方箋①:OKRで全社のベクトルを合わせる 3-1. OKR(Objectives and Key Results)フレームワークの導入 3-2. AIプロジェクトにこそ「SMART」な目標を 3-3. IBMやGoogleの事例に学ぶ 第4章:処方箋②:DX推進の中核組織「CoE」を立ち上げる 4-1. DX CoE(Center of Excellence)とは? 4-2. CoEが担う4つの重要機能 4-3. あなたの会社に最適なCoEの形は? 第5章:処方箋③:「リスキリング」で社内に眠る才能を開花させる 5-1. まずは全社員の「共通言語」を作る(基礎リテラシー層) 5-2. 次世代リーダーを選抜し、武者修行させる(専門家/リーダー層) 5-3. 現場のヒーロー「市民開発者」を育てる(実践者層) 第6章:処方箋④:「社内特区」でイノベーションの実験場を作る 6-1. 「AI/DX特区制度(社内サンドボックス)」の創設 6-2. 「ハンコのための出社」をなくす稟議・承認プロセスの改革 6-3. 「ゼロトラスト」で安全と速度を両立する まとめ:AIドリブンな組織への変革は、今日から始められる その「AI導入」、本当に進んでいますか? 「全社で生成AIを活用するぞ!」 経営陣の号令のもと、鳴り物入りでスタートしたDXプロジェクト。しかし、数ヶ月経った今、こんな課題に直面していませんか? 各プロジェクトが並行してバラバラで進行しており、全体として何を目指しているのか分からない… 誰が責任者で、どういう指示系統の元進めていくのかが曖昧でプロジェクトがうまく進まない… 推進できる人材が社内におらず、現場が闇雲の中で疲弊している… 新しいツールを使いたいだけなのに、社内申請にかなりの時間がかかる… もし一つでも当てはまったなら、ご安心ください。それはあなたの会社だけの問題ではありません。多くの企業が、大規模な変革の過程で同じような「成長痛」を経験しています。 この問題の本質は、生成AIという最新の「ソフトウェア」を、旧来の「組織OS」の上で無理に動かそうとしていることにあります。 本記事では、この摩擦を解消し、AI活用を真に加速させるための「組織OSのアップグレード方法」を、4つの具体的な処方箋としてご紹介します。 第1章:生成AI推進を阻む「4つの壁」とその正体 まず、多くの企業が直面する4つの課題を深掘りしてみましょう。これらは個別の問題ではなく、互いに絡み合い、変革のブレーキとなる厄介な壁を築いています。 1-1. 羅針盤なき航海:「とりあえずDX」の罠 「まずはやってみよう」と、明確なゴール設定よりも目先の実行しやすさを優先してプロジェクトを始めていませんか?これは変革の初期段階でよく見られる光景ですが、中盤になると「このプロジェクト、どこに向かってるんだっけ?」と、方向性を見失い、意思決定ができない「カオス状態」に陥りがちです 。 生成AIの進化は速く、長期的なゴール設定は難しいもの。しかし、だからといって指針がなければ、それはただの漂流です 。「レポート作成時間を50%削減する」といった、測れるビジネス成果とプロジェクトが結びついていない場合、問題はさらに深刻化します 。 仮に走りながら意思決定をしていくというプロセスも状況によっては間違いではないですが だとしても「とりあえずDX」から「計画的DX」へと昇華させるための修正・アップデートを進める機関やフローも同時に整備していかなければなりません。 1-2. 構造的ボトルネック:DXを阻む旧態依然の組織体制 現在の組織構造は、DXのスピード感に対応できていますか?意思決定の権限や責任の所在が曖昧で、複数のプロジェクトを横断して見ている人がいない…。そんな状態では、部門間の連携は生まれず、組織のサイロ化とスピードの低下を招くだけです。 特に大企業では、既存の複雑なプロセスが新しい挑戦の足枷となる「組織の硬直化」が起こりがちです 。DXを成功させるには、十分な権限を持つCDO(Chief Digital Officer)のような明確なリーダーと、専門の推進体制が不可欠なのです 。 1-3. 人材の枯渇:誰もいない「推進リーダー」の席 AIプロジェクトを力強く引っ張っていけるリーダーが社内にいますか?この問題は、組織体制の問題と深く関わっています。専門の推進組織がなければ、そうした人材が育つキャリアパスもなく、結果としてリーダー不在のプロジェクトが乱立してしまいます。 「DX人材不足」は日本中の企業が抱える課題ですが 、単に技術者が足りないという話ではありません。本当に深刻なのは、技術とビジネスをつなぐプロジェクトマネージャーやビジネスリーダーがいないことなのです 。 1-2で述べた組織体制にも通じる話ですが多くの企業はここを「ベンダー」側に完全委託してしまうケースが多いです。 1-4. イノベーションの足枷:変化を拒む社内ルール 「新しいツールを試したいのに、申請に2ヶ月もかかる…」 こんな経験はありませんか?安定した事業運営を前提に作られた社内ルールは、トライアンドエラーが必須のAI開発とは相性が最悪です。まるで、身体の「免疫システム」が新しい変化を拒絶しているかのよう。既存のやり方を守ることが優先され、イノベーションの芽が摘まれてしまっているのです 。 これら4つの問題は、互いに影響し合い、「ドゥームループ(破滅の連鎖)」と呼ばれる負のスパイラルを生み出します。推進体制がない(問題2)から人材が育たず(問題3)、リーダー不在でプロジェクトが迷走し(問題1)、成果が出ないことで組織はさらに保守的になり、ルールを強化する(問題4)。この悪循環を断ち切るには、包括的なアプローチが必要です。 第2章:解決の鍵は「アジャイル・ガバナンス」という新しい考え方 これらの根深い問題を解決するには、小手先の修正では不十分です。組織の意思決定とリスク管理のあり方そのものを変える、新しいOSが必要になります。そこで「アジャイル・ガバナンス」と最近呼ばれている仕組みについてご紹介させていただきます。 参考資料: https://biz.moneyforward.com/ipo/basic/10334/ これは、従来の「制限」や「管理」のためのガバナンスではありません。不確実な時代に「イノベーションを可能にする」ための、柔軟で動的なフレームワークです。ビジネス、IT、法務、セキュリティなど、多様な関係者が協力し、走りながらルールを改善していく。そんな新しいガバナンスの形です 。 アジャイル・ガバナンスは、予測不可能なAI開発を、旧来のガバナンスで管理しようとする「根本的なミスマッチ」を解消します。そして、前述の4つの課題をまとめて解決へと導きます。 目的の明確化(問題1):ゴールを設定しつつ、柔軟な見直しを許容する。 組織体制(問題2):多様な関係者を巻き込み、協働を促す。 人材不足(問題3):新しい専門家の役割を定義し、活躍の場を作る。 硬直化したルール(問題4):ルールを固定せず、継続的に更新する。 これは単なるプロセス変更ではなく、「失敗を恐れる文化」から「失敗から素早く学ぶ文化」への転換を意味します。次章からは上記の4つの課題それぞれに対しての処方箋について述べていきます。 第3章:処方箋①:OKRで全社のベクトルを合わせる 「結局、何を目指すんだっけ?」というカオス状態を抜け出すため、全社的な目標と各プロジェクトを明確に連携させるフレームワークを導入しましょう。 3-1. OKR(Objectives and Key Results)フレームワークの導入 Googleやメルカリなども採用する目標設定・管理手法「OKR」を導入します 。OKRは、ワクワクするような定性的な「目標(Objectives)」と、その達成度を測る定量的な「主要な成果(Key Results)」で構成されます。 目標(O): 「社内のナレッジ共有を革新する」といった、挑戦的で心躍るゴール。 主要な成果(KR): 「重要文書の検索時間を30%削減する」「新ナレッジ基盤のユーザー満足度80%を達成する」といった、具体的で測定可能な指標 。 OKRの素晴らしい点は、経営層から現場まで、組織全体の目標を一本化しつつ、現場の自律性を尊重できることです。トップダウンの戦略と、現場の主体性。DX推進におけるこのジレンマを解消する強力なツールとなります。 よくありがちな事例としては挑戦的な目標であるOの部分はかなり大々的に発令されていて一見DXとしての温度感は全体として高いように見えます。しかし実態としてKRが正しく紐づいて設定されておらず、推進という観点では現場の主体性が損なわれ、いつの間にかOについての解釈がチームや個人ごとに異なる、その結果目指すべき方向性のずれやプロジェクトのKPIとしての指針を見失っていくというケースは多く見てきました。 3-2. AIプロジェクトにこそ「SMART」な目標を OKRを機能させるため、すべての主要な成果(KR)が「SMART原則」(Specific:具体的、Measurable:測定可能、Achievable:達成可能、Relevant:関連性がある、Time-bound:期限がある)を満たすようにします 。 「業務効率を改善する」といった曖昧な目標ではなく、「特定の月次レポート5種類のドラフト作成を第3四半期までに自動化し、精度90%を達成する」といった、誰が見ても進捗がわかる具体的な目標に落とし込むことが、カオスを防ぐ鍵です 。 SMARTな設計ができていないとプロジェクト推進の中盤での後戻りが発生する可能性や、今やっていることの重要性を見失う、結果プロジェクト全体としての軸を改めて定め直すといったことも起こりがちです。 3-3. IBMやGoogleの事例に学ぶ IBMの大規模システム導入事例では、IT、営業、プロジェクトマネージャーといった異なるチームが、全社目標に連携したOKRをどう設定したかが示されています 。部門横断プロジェクトで、各チームが自分の役割を理解しつつ、同じゴールに向かうための優れたお手本となるでしょう。 OKRの導入は、単なる管理手法の変更ではありません。それは、「何をもって成功とするか」を組織全体で真剣に問い直す、文化的な変革となります。 大規模なDX推進の現場ではさまざまな部署から集められた人員によってチームが組成され推進していくケースも少なくありません。しかし本来チームや部署によってプロジェクトに対する温度感やKPI、MBOの設定などは多種多様であり、元々は同じ方向を向いていないケースが多いです。 これをそのままにしておくと、メンバーによってのモチベーションや自発性などに温度感の差異が起こり、部署間のコンフリクトや推進の鈍化が起こります。それらをいかに全社目標に連携させて設定していくかが重要な鍵となります。 参考: https://www.ibm.com/jp-ja/think/topics/okr-examples 第4章:処方箋②:DX推進の中核組織「CoE」を立ち上げる 変革の強力なエンジンとなる専門組織「DX CoE(Center of Excellence)」を設立し、現在の曖昧な意思決定構造と部門間のサイロを破壊しましょう。 4-1. DX CoE(Center of Excellence)とは? CoEは、単なるIT部門ではありません。ビジネス、テクノロジー、データ分析など、多様な専門家が集結した、組織横断の戦略チームです 。社内に散らばった知識や取り組みを集約し、全社を俯瞰する「司令塔」として機能します 。 4-2. CoEが担う4つの重要機能 ナレッジ集約: 社内外の成功事例や教訓を集め、全社に共有し、組織の学習スピードを加速させます 。 ガバナンス: アジャイル開発手法や評価指標(KPI)など、全社的な標準ルールを作り、プロジェクトの一貫性を保ちます 。 技術支援: 各部門のプロジェクトに対し、専門知識を提供し、社内コンサルタントとして課題解決をサポートします 。 人材育成: 人事部と連携し、次の章で紹介する全社的なリスキリングを主導します 。 CoEの成功は、CoEが支援したプロジェクトの成功率や、プロジェクト期間の短縮率といった具体的な貢献度で測るべきです 。 4-3. あなたの会社に最適なCoEの形は? CoEにはいくつかのモデルがあり一部を紹介します。私が現場で携わっている範疇においては連携・ハイブリッド型の事例が多いです。 モデル種別 説明 長所 短所 中央集権型 強力な中央組織が全社のDXを統制する。 ・迅速な標準化 ・強力なガバナンス ・現場ニーズとの乖離 ・官僚化のリスク 連携・ハイブリッド型 中央のDX本部が戦略や基盤を提供し、各事業部門と連携して推進する。 ・中央戦略と現場ニーズの両立 ・現場の主体性の尊重 ・役割分担の明確化が必要 ・調整コストが発生 このモデルでは、CoEが戦略的な方向性を示しつつ、事業部門が現場の知識を活かして主体的にイノベーションを起こせます。CoEの最も重要な役割は、経営の言葉を技術の言葉に、技術の可能性をビジネスの言葉に「翻訳」することであり、この翻訳機能こそが部門間の壁を壊し、DXの成功確率を飛躍的に高めるのです。 第5章:処方箋③:「リスキリング」で社内に眠る才能を開花させる DX人材は、外から採用するだけでは足りません。社内の人材を戦略的に育成する「リスキリング」こそが、持続可能な変革の鍵です。全従業員を対象とした階層的な育成プログラムを始めましょう。 5-1. まずは全社員の「共通言語」を作る(基礎リテラシー層) 経営層も含めた全従業員を対象に、DX/AIリテラシーの基礎研修を必須化します。eラーニングなどを活用し、AIの基本的な仕組みや可能性、倫理的な注意点について、全社で共通の理解を醸成しましょう 。全社員に教育を義務付けることで、変革の土壌を一気に耕すことができます 。 5-2. 次世代リーダーを選抜し、武者修行させる(専門家/リーダー層) ポテンシャルの高い人材を選び、次世代のDXリーダーを育成する特別プログラムを創設します。技術だけでなく、プロジェクトマネジメントや変革を主導するリーダーシップを総合的に学びます。いわゆるラウドスピーカーと呼ばれる彼らを集中的に育成していくことでAIに対する社内認知や行動意欲を波及効果で拡大させることが可能になり、よりDXの文化醸成が活発になります。 5-3. 現場のヒーロー「市民開発者」を育てる(実践者層) 事業部門の従業員が、プログラミング不要の「ノーコード・ローコードツール」などを使い、自らの手で業務改善を進められるように支援します。現場の深い業務知識と新しいデジタルツールが結びついた時、大きなイノベーションが生まれます。これは、トップダウンで進むDXを、現場にとって「自分事」にするための最も有効なアプローチです。 階層レベル 対象者 主要な学習目標 カリキュラム・手法の例 基礎リテラシー層 全従業員 ・AI/DXの基本概念の理解 ・自社DX戦略の理解 ・eラーニング(必修) ・全社ワークショップ 実践者層 各部門の業務担当者 ・ノーコード/ローコードツール活用 ・データ分析・可視化スキル ・ツール別ハンズオン研修 ・社内ハッカソン 専門家/リーダー層 選抜された人材 ・アジャイルプロジェクト管理 ・チェンジマネジメント ・社内大学(選抜制) ・越境学習(他社派遣等) 基礎リテラシー層への全体的なAIナレッジの展開を経てAIを使っていく意識醸成を進め、それらの推進、ないしは実践者層への業務導入を専門家・リーダー層が強力に進めていく。この流れを構築していくことが文化醸成、強いてはAI浸透と強固な組織レベル底上げに必要な要素となります。 第6章:処方箋④:「社内特区」でイノベーションの実験場を作る イノベーションの足枷となっている硬直的な社内ルールを、思い切って変えていきましょう。実験を奨励し、スピードを加速させるための制度改革です。 6-1. 「AI/DX特区制度(社内サンドボックス)」の創設 社内に、実験的なプロジェクトのための「AI/DX特区」、すなわち社内サンドボックスを設けましょう。これは、認定されたプロジェクトに限り、通常の煩雑な手続きを免除・簡素化する特別なエリアです 。 何ができる?: この特区内では、新しいクラウドサービスの利用申請が迅速化されたり、実験専用のセキュアな環境が提供されたりします 。 どう管理する?: CoEがプロジェクトを審査・監督することで、自由な実験と組織的な統制のバランスを取ります。 6-2. 「ハンコのための出社」をなくす稟議・承認プロセスの改革 全社的なボトルネックである稟議プロセスを、単に電子化するだけでなく、プロセスそのものを見直しましょう 。「この承認は本当に必要か?」「現場の決裁権限を増やせないか?」といった視点で、不要なステップを大胆に削減することが重要です 。 6-3. 「ゼロトラスト」で安全と速度を両立する セキュリティの考え方を、社内は安全、社外は危険という「城と堀」モデルから、「誰も信頼せず、常に検証する」という「ゼロトラスト」モデルへ移行しましょう 。これにより、従業員はどこからでも安全に必要なツールにアクセスでき、アジャイルな働き方を強力にサポートします。これはNTTデータ先端技術やLIXILなども取り組む、現代の必須インフラです 。 この「社内サンドボックス」は、単なる技術の実験場ではありません。それは「新しいガバナンスのプロトタイプ」です。サンドボックス内で試された新しいルールが成功すれば、いずれ全社標準へと展開していく。安全な環境で、未来の会社のルールを生み出していくための、極めて重要な仕組みなのです。 まとめ:AIドリブンな組織への変革は、今日から始められる 本記事では、生成AI推進の過程で多くの企業が直面する4つの壁と、それを乗り越えるための4つの処方箋をご紹介しました。 目的の明確化: OKRで全社のベクトルを合わせる 体制の改革: DX CoEを設立し、変革のエンジンを作る 人材の育成: リスキリングで社内の才能を解き放つ 制度・環境の整備: 社内サンドボックスでイノベーションを加速する これらは、一つひとつが独立した施策ではなく、相互に連携することで最大の効果を発揮します。とはいえ、すべてを一度に始める必要はありません。まずは、あなたの組織で最も着手しやすいところから、小さな一歩を踏み出してみてはいかがでしょうか。 まずは3ヶ月でできること 経営層を巻き込み、小さなCoEチームを発足させる。 特に重要なパイロットプロジェクトで、OKRを試してみる。 そのプロジェクト限定の「社内サンドボックス」を設計してみる。 今直面している課題は、未来の競争優位性を築くための絶好の機会です。この記事が、あなたの会社の変革を後押しする一助となれば幸いです。
エンジニア集団の中に潜む非エンジニアの生態 はじめまして。Insight Edgeセールス・コンサルティングチームで契約業務や売上管理を担当している非エンジニアの長尾です。 周りを見渡せば、AIやデータサイエンスの博士号を持つメンバーや、大規模なシステム開発を率いてきた猛者ばかり。そんな技術のプロフェッショナル集団の中で、私はコードを書かない「非エンジニア」として働いています。 私の周りでは、日常会話で「fetchするためのMCPサーバを...」や「LLMによるペルソナ生成のプロンプトが…」といった言葉が飛び交います。それを聞きながら「今はポジティブな話?それともネガティブな話…?」と、話の趣旨すら掴めないことも。 今日は、そんな私が専門外の領域でいかにして価値を見出し、課題解決に挑んだのか。そして、ローコードツール「Dify」を使い、まずは自社の案件検索を効率化するAIツールを自力で作り上げた話をさせてください。これは、私個人の奮闘記であると同時に、私たちの会社Insight Edgeが持つ「やってみる」という文化の証明でもあります。 エンジニア集団の中に潜む非エンジニアの生態 1. 「あの情報、どこだっけ?」- 日々の業務に潜む巨大な時間泥棒 2. やってみる、から始まった挑戦 3. ゼロからのAI開発ジャーニー:非エンジニアが挑んだ3週間 ステップ1:AIに知識を叩き込む(ナレッジベースの構築) ステップ2:AIとの対話方法を教える(プロンプトエンジニアリング) ステップ3:ひたすら試し、改善する 4. コードを書かない私が描く、次の景色 1. 「あの情報、どこだっけ?」- 日々の業務に潜む巨大な時間泥棒 私の主な業務は、お客様との契約締結や進行中のプロジェクトの売上管理です。一見、華やかなデータ分析やAI開発の世界とは少し離れた「守り」の領域かもしれません。しかし、この業務には大きな課題が潜んでいました。それは、「情報のサイロ化」です。 「あの案件で使った契約書の文言、今回も流用できそうだな…」 「去年とほぼ同じ内容の案件、プロジェクト名は何だったっけ…」 こうした情報を探すたびに、社内のストレージ、チャットツールなど、様々な格納先を何十分も彷徨う日々。情報は確かにあるのに、必要な時にすぐに見つけられない。この“時間泥棒”は、私の生産性を着実に蝕んでいました。 エンジニアたちが最新技術で顧客の課題を解決している横で、私は社内の情報検索という原始的な課題に頭を悩ませていました。この状況を、何とかしたい。それがすべての始まりでした。 2. やってみる、から始まった挑戦 課題を嘆くだけでは何も変わりません。Insight Edgeに入社してまもなく1年、この会社で見聞きしてきたことを糧に、自ら行動を起こすことを決意しました。幸い、当社には職種や経験を問わず、課題意識を持って新しい挑戦を奨励する「やってみる」という素晴らしい文化が根付いています。 まず頭に浮かんだのは、「ChatGPTのようなAIに、社内案件のことを聞けたら最高じゃないか?」という素朴なアイデアでした。 しかし、すぐに壁にぶつかります。ChatGPTは、当然ながら社内の機密情報や最新のファイルを知りません。次に、社内情報と連携したGoogleのAIエージェントを試してみましたが、あらゆる情報ソースを参照するがゆえに、近いですが異なる情報を拾われることが多く私が求める粒度と精度での回答は得られませんでした。 私が欲しいのは、博識なAIではなく、「ウチの会社の事情」に詳しいAIアシスタントだったのです。 この課題を解決する鍵は「RAG(Retrieval-Augmented Generation)」という技術にあると突き止めました。これは、AIに外部の知識(今回の場合は社内ドキュメント)を「参照」させながら回答を生成させる仕組みです。非エンジニアの私にも分かるように言うなれば、「分厚い資料集を横に置き、常にそれを見ながら質問に答えてくれるサポーター」といったイメージです。 RAGの概念は理解できても、実装はできない。そんな私の前に現れたのが、ノーコード/ローコードでAIアプリを構築できるプラットフォーム「Dify」でした。これなら、私でもAIアシスタントを作れるかもしれない。暗闇に光が差した瞬間でした。 3. ゼロからのAI開発ジャーニー:非エンジニアが挑んだ3週間 Difyと出会ってからの行動は早かったです。「意外とすぐにできるかも?」という淡い期待を胸に、AIツール開発に着手しました。 ステップ1:AIに知識を叩き込む(ナレッジベースの構築) まずはAIの「資料集」となるナレッジベースの構築です。ソースとなるファイルから機密情報を削除し、Difyにアップロードしていきます。しかし、すぐに最初の壁にぶつかりました。取り込んだ情報をもとに質問しても、まともな答えが返ってこないのです。 原因を調べると、AIが正しく読み込めるようにデータを「成形」する必要があることに行き着きました。1行目をヘッダーにする、セルの結合をなくす、不要な空白を削除するなど、エンジニアにとっては当たり前かもしれないルールに悪戦苦闘。ひとつひとつデータを整え、無事にナレッジベースを完成させました。 ステップ2:AIとの対話方法を教える(プロンプトエンジニアリング) 次に、AIへの指示、つまり「プロンプト」の設計です。「あなたは、提供されたデータを分析する優秀なアシスタントです」といった役割設定から、「データにない情報や推測で回答してはいけません」といった禁止事項(ガードレール)まで、試行錯誤を繰り返しました。このチューニング作業は、まるでゲームでキャラクターを育成するような面白さがあり、先人たちが築いた定石(ベストプラクティス)を参考にすることが成功への近道である点も似ています。 ステップ3:ひたすら試し、改善する プロトタイプが完成してからは、あらゆる角度から質問を投げかけ、回答の精度を検証しました。生成AIは数値を扱うのが少し苦手、といった特性もこの過程で理解しました。精度が悪い部分があればステップ1と2に戻って修正を繰り返し、最終的に、求める粒度の質問に対して約90%の精度で回答してくれるツールが完成しました。 もちろん、専門的な壁にぶつかることもありました。そんな時は、社内のエンジニアに「ここについて教えてください!」とチャットで助けを求めます。すると、「Difyは使ったことないけど、ちょっと待ってて」と言いながら、数分後には的確なアドバイスが返ってくる。挑戦の過程で生まれた壁は、誰もが快く一緒に乗り越えようとしてくれる。これも、この会社の「みんなでやる」という文化の現れだと感じています。 4. コードを書かない私が描く、次の景色 約3週間の試行錯誤の末、ついに案件情報検索AIが完成しました。 これまで週に数時間かかっていた情報の捜索は、今では1件あたり数秒で完了します。当初の目的だった「業務効率化」と「情報のサイロ化改善」は、無事に達成されました。しかし、得られたものはそれだけではありません。自分でものづくりのプロセスを経験したことで、「次はこんなこともできるかも」と、新たなアイデアが次々と湧いてくるようになったのです。 この経験を通じて私が学んだのは、課題解決の手段は、必ずしもコードを書くことだけではない、ということです。非エンジニアだからこそ見える現場の課題があり、非エンジニアでも使えるツールがある。そして、その挑戦を支えてくれる仲間と文化がある。 Insight Edgeは、AIやデータという最先端技術を駆使して、お客様のビジネスと世界を「Re-design」することを目指す会社です。私の小さな挑戦は、その壮大なミッションのほんの一端かもしれません。しかし、現場の課題に深く潜り込み、テクノロジーを武器にそれを解決するという点で、本質は同じだと信じています。 もしあなたが、私のように専門外の領域で自分の価値をどう発揮すべきか悩んでいるなら、思い出してください。あなたの視点こそが、ブレイクスルーの鍵になります。 なぜなら、あなたは誰よりもその課題の「解像度」が高い当事者だからです。現場の人間だからこそ、「これじゃない感」のある片手落ちの効率化に留まらず、「こうあるべきだ」という理想から逆算して根本的な解決策を追求できます。 今の時代、その理想を現実にするための武器は、驚くほど身近な場所にあります。大事なのは、「誰かがやってくれる」のを待つのではなく、自らが最初の実践者になるという「やってみる」勇気です。 その一歩が、目の前の課題を解決するだけでなく、あなた自身の「やってみたい」という新たな景色を見せてくれるはずです。
プロローグ:この記事を書くことになったきっかけ 今回の記事は、Insight Edgeでデザインストラテジストを務める飯伏さんと、AIである私との対話から生まれました。 実は2年前にも飯伏さんは自らの仕事についてテックブログにまとめていました。そのときは「課題探索やアイデア発想を支援するデザインシンカー」としての役割紹介でした( デザインシンカーとしての仕事 ~DX推進の技術専門会社にて~ - Insight Edge Tech Blog )。 そこから2年、生成AIの登場と普及、住友商事グループにおけるデジタル推進の加速、そして事業会社の自走意識の高まりなど、DXを取り巻く環境は大きく変化しました。 こうした変化の中で「仕事の幅がどう進化したのか」を改めて整理したい──そんな飯伏さんの思いから、今回の対談記事が始まりました。 導入 AI: 今日は「デザインストラテジスト」という少し耳慣れない肩書きのお仕事について伺います。 もともとこの役割は海外のデザイン会社やグローバル企業で広がったポジションで、ユーザーリサーチや未来洞察を通じて「事業や組織の将来像を描き、実現するための道筋をデザインする」仕事です。 たとえば米国や北欧の企業では、経営戦略の段階からデザインストラテジストが参画し、ユーザー視点とビジネス視点を橋渡しする事例も多く見られます。日本でも大手企業やデザインファームで少しずつ導入され始めていますが、まだ一般的には馴染みの薄い職種かもしれません。 今回はその中でも、技術専門会社であるInsight Edgeにおけるデザインストラテジストの実際の仕事を飯伏さんに紹介していただきたいと思います。 飯伏: よろしくお願いします。私はInsight Edgeで「人や組織の視点からDXを前に進める」役割を担っています。今日はその内容を具体的にお話しできればと思います。 会社での立ち位置 AI: DXを技術で進める人はイメージしやすいですが、「人や組織の視点から前に進める」というのは少し抽象的ですね。具体的にどんな枠組みで仕事をしているんですか? 飯伏: 大きく分けると2つです。 ひとつは、事業会社のDX推進・AI活用を担う専門組織(いわゆるCoE=センター・オブ・エクセレンス)の立ち上げや継続に伴走する支援。 もうひとつは、必要なタイミングで変革や技術活用を後押しするピンポイントの支援です。 仕事①:CoE的な組織への伴走支援 AI: まずは前者から詳しく伺いたいです。CoEというのは「全社的にDXやAIを推進していくための専門チーム」のことですよね。そこへの伴走ではどんなことをされるのでしょう? 飯伏: 大きく「戦略づくり」「実装支援」「浸透・醸成」の3つに分けて関わります。 戦略づくり DX推進やAI活用を担う組織が「どんな姿をめざすのか」を描き、部門横断のビジョンやロードマップに落とし込みます。 具体的には、経営層や現場担当者へのインタビュー、未来トレンドの探索などを組み合わせて「現状の延長線」ではなく「理想の将来像」を共に構想します。 あるグループ企業では、データ活用推進部門と共に「部門自体の理想像」や「各部門の重点テーマ」をワークショップで整理し、3年の中期的な将来像やロードマップ、1年間の具体計画にまとめました。 実装支援 ここは2年前から取り組んでいる範囲ですね。 戦略を「絵に描いた餅」で終わらせず、現場での実装に結びつけるため、データ分析やAI活用の企画を現場と共創し、ソリューションを具体化します。 その際には、利用者となる現場担当者との対話を重ね、Asis/Tobe像をビジュアルで整理。発散と収束を繰り返しながら「本当に必要な価値」と実現方法を具体化していきます。 フェーズが進むほど専門性が増すため、エンジニアやUI/UXデザイナーも早期から一体で取り組むようにしています。 浸透・醸成 短期施策を積み重ねながらDXを文化として根付かせ、中長期的に自走できる組織を育てます。 あるCoEでは「増やすべき人材像」の検討や、AI活用のハンズオン研修、DX最新動向を伝えるセミナーを企画。最近はチームメンバーも増え、システム思考など新たなアプローチを取り込みながら挑戦を広げています。 AI: 単なるコンサルティングではなく、立ち上げ時期の戦略から実装、中長期でのDXやAIの推進の醸成まで伴走するのが特徴なんですね。 飯伏: はい、まさにそうです。 ただ一点補足で、立ち上げ時期に戦略づくりから始める印象がありますが、実態は「頭でっかちな検討はいらないから、速く/早く使えるものを作って現場を変えていこう」という期待も多く、実装支援に重きを置いて戦略づくりと並行して進めることが多いですね。 仕事②:ピンポイントの支援 AI: もうひとつの軸、ピンポイント支援についても教えてください。こちらは「必要なタイミングで変革や技術活用を後押しする」と伺いました。 飯伏: そうです。 例えばあるグループ企業では、AIを活用する事業構想とコミュニケーション活性化を目的に、未来洞察をベースとしたアイデア発想のワークショップを実施しました。普段の業務とは異なり、現状ではなく未来を起点とした発想で、AIの活用についてアイデアを広げました。他には、複数日にわたるワークショップで事業構想に取り組むこともあります。 また別のグループ企業では、AIを活用したアプリのプロトタイピング(バイブコーディング)を現場で広く実践していくことを目的に、数週間にわたる道場形式のハンズオンワークショップを企画しています。 AI: なるほど。伴走支援が「じっくり支える」だとすれば、ピンポイント支援は「狙いを絞って次のアクションを後押しする」役割なんですね。 飯伏: はい、その表現はしっくりきます。 2年前からの進化 AI: ちなみに、 2年前にもブログ を書かれていましたよね。当時はどんな仕事を中心にされていたんですか? 飯伏: その頃は「課題探索やアイデア発想の支援」が中心でした。特定の取り組みテーマや対象業務の領域は決まっているものの、具体的に何をしていくのかがモヤモヤしている。そのような状態から整理してPoCにつなげるのが主な役割でしたね。 AI: そこから2年で、CoE的な組織への伴走支援での戦略・実装・醸成や、ピンポイントの変革や技術活用の後押しまで仕事が広がった背景には何があるんでしょう? 飯伏: 大きく3つあると思います。 1つ目は、生成AIの発展で、技術活用の可能性・危機感が一層広がったこと。 2つ目は、1つ目に関連しますが、事業会社の自走意識の高まりです。「現場実装を自ら進める」というマインドが浸透してきました。 3つ目は、住友商事グループとしてデジタル領域のより一層の強化に取り組まれていることです。ここは、住友商事グループで最新技術から内製機能まで提供するInsight Edgeの位置づけとしても重要なポイントです。 AI: ここまででも十分大きな変化ですね。ただ、飯伏さんのお話からすると「フィールドの広がり」も大きな要素のように思えますが、いかがですか? 飯伏: そうですね。元々、住友商事グループは多様な事業領域を持っています。その中で業種も文化も異なる現場に関わることで、得られる視点や学びが増え、次の取り組みに活かされると実感しています。つまりフィールドの豊かさが、進化させてくれたとも思います。 大事にしていること AI: これまでのお話をとおして、経営や技術、特定のフレームワーク以上に「人に寄り添う」スタンスが軸にあるように感じます。 飯伏: まさにそこが一番大事です。私は「人や組織の想いに寄り添いながらモヤモヤを整理し、“前に進める一歩”を形にする」ことを常に意識しています。 ここで大事なのは、答えを押しつけることではなく、共に考え続けること。ときには対話を通じて価値観を引き出し、ときには未来を描くワークで可能性を広げ、ときには泥臭く詳細な整理・検証に取り組みます。 住友商事グループという多様なフィールドで仕事をするからこそ、この姿勢がより大切だと感じます。業種も組織文化も異なる人々に向き合うと、共通点も違いも浮かび上がる。その中で「どうすれば一緒に前に進めるか、一緒に挑戦を実現していけるか」をデザインする。まだまだ住友商事グループの中でも、デザインストラテジストのような広義のデザインは浸透していませんが、デザインのアプローチを武器にこのフィールドで取り組むのは大きなやりがいです。 AI: なるほど。つまり「デザイン」は表層の見た目を整えることではなく、異なる人や組織をつなぎ、動き出せる状態をつくる営みだということですね? 飯伏: そうです。デザインストラテジストは、単に新しい仕組みやサービスを導入する役割ではなく、組織が変化・自走できる力を育む存在。そのために私は、現場の人の想いと組織の現実、そして経営や技術を結びつけ、“前に進める一歩”を一緒に生みだす場・機会のデザインを大切にしています。 AIとの協働 AI: ここまで伺っていると、人や組織と向き合う仕事が中心ですが、実際にはAIとも一緒に仕事をされていますよね。 飯伏: そうですね。AIは単なる調べ物のツールではなく、それこそ“共創パートナー”のような存在です。 たとえば、経営層向けの「AIを活用した事業構想を検討するワークショップ」を設計するとき、私が持っていたイメージを投げかけると、AIが具体的な流れを整理して案を複数パターン作ってくれました。それを叩き台に現場感覚で磨き込むことで、実践的なプログラムに仕上げられたんです。 ほかにも、アイデアを整理する際のフレームづくりや壁打ち、デザインストラテジストの採用検討なども一緒に取り組みました。 AIが出してくれるのは“考えるきっかけ”や“俯瞰の視点”。そこに現場の肌感覚を掛け合わせることで、速くよりよい形に仕上がる実感があります。デザインストラテジストとして技術をどう活かすか考える立場としても、AIとの共創はますます欠かせないと感じています。 今後に向けて AI: 最後に、今後の展望を教えてください。 飯伏: これからもDX推進に関わりながら、生成AIやデータ分析を当たり前とした新しいデザインのアプローチも試していきたいと思っています。そして「テクノロジーとクリエイティブの力で、変革を続ける組織を増やす」ことが、デザインストラテジストとしての目標です。 AI: 今日はありがとうございました。Insight Edgeの中にこうした役割を担う方がいることを知っていただけると、読者の方にも新しい発見になると思います。 飯伏: こちらこそ、ありがとうございました。 エピローグ:対話を通じて見えてきたこと 今回の対談を通じて見えてきたのは、「デザインストラテジスト」という役割が決して一人で完結する仕事ではない、ということです。 人や組織の想いを丁寧に汲み取りながら進める仕事であると同時に、AIのようなテクノロジーとも協働しながら新しい道筋を描いていく──それが今の時代のデザインストラテジストの姿なのかもしれません。 また、2年前に「課題探索やアイデア支援」として紹介した仕事は、この2年間で伴走による「戦略づくり」「実装支援」「浸透・醸成」へと広がりました。 印象的だったのは、こうした広がりの中で「デザイン的なアプローチ」そのものの価値が、以前よりも一層強く発揮されていることです。それは単に美しいビジョンを描くことではなく、複雑な利害や制約の中で対話を重ね、納得して動き出せる道筋をデザインすることでもあります。曖昧な状況を整理し、共創を通じて前に進める一歩を形にする力が、DXやAI活用を進める現場で確かな意味を持ち始めているように感じました。 読者の皆さんにとってこの記事が、DXやAI活用に向き合うときに「人と組織をどう巻き込み、どう共創していくか」を考えるヒントになれば幸いです。 ──なお本記事は、これまでの飯伏さんとのやり取りをもとにAIが下書きを出力し、それを飯伏さんが手直しする形で仕上げられています。まさに「AIとの協働」の一例として読んでいただければと思います。
はじめに わずか3日で開発して稼働開始、そして1年間トラブルゼロ。 普通なら半年〜1年かかる開発も、Insight Edgeのデータサイエンティストとエンジニアは、ワンチームで動き、爆速で価値をクライアント企業に届けています。 仕様書の山も、開発ベンダーとの往復メールもありません。 モデルを作ったらアジャイル方式で即アプリ化し、 クラウドにデプロイして、翌日にはクライアントが使い始めることもあります。 この記事では、そんな爆速開発を可能にしている データサイエンティスト×エンジニアの共同開発 の事例を3つ紹介し、最後にポイントをまとめます。 目次 事例1:売上予測アプリを2ヶ月でリリース 事例2:3日で完成!S3+Lambdaだけの軽量予測システム 事例3:10時間かかっていた遺伝的アルゴリズムを並列分散処理で高速化 爆速開発を可能にする3つの秘密 事例1:売上予測アプリを2ヶ月でリリース PoCで作成した売り上げ予測モデルが一定の性能を満たし、クライアントから「現場ですぐに試したい」という要望が寄せられました。早く、安く、安全にアプリ化する必要がある状況です。 そこでまず、データサイエンティストがざっくりとしたUI案とアーキテクチャ図をパワポで作成しました。それをエンジニアに見せ、「こういうものを作れないか」と相談します。 すると、エンジニアはすぐにAWSのアーキテクチャ案と工数・運用費の見積もりを返してくれました。そのスピード感もあって、提案はスムーズに承認されました。 開発は同じGitリポジトリ上で進めました。 データサイエンティストは予測モデルを呼び出すクラスのインターフェースとサンプルコードを、実装前にMkDocsでドキュメント化して事前に共有。 そうすることで、エンジニアとデータサイエンティストはそれぞれの担当部分を並行して開発できました。 Daily MTGは合同で行い、週次でクライアントとのMTGでフィードバックを受け、それを即反映。 ベンダー向けの仕様書の山を作ることもQAのラリーを大量に行う必要もなく、アジャイルな形で進められました。 結果、プロジェクト開始からわずか2ヶ月で現場利用が開始され、クライアントの満足度も高く、ベンダー委託時にありがちな遅延やコスト増は一切ありませんでした。 事例2:3日で完成!S3+Lambdaだけの軽量予測システム ある案件では、「UIは不要、最低限の機能で良い」という要望がありました。クライアントはとにかく早く動くものを試したいという状況でした。 そこでデータサイエンティストは、わずか一行で要件をエンジニアに伝えました。 「S3にファイルを置いたらLambdaが起動して、予測してS3に結果を返す感じで作りたいんです」 エンジニアはすぐ設計に着手し、S3へのアップロードをトリガーにLambdaが動き、Fargateで予測を実行して結果を保存するというシンプルな構成を実装しました。 ※実行時間がLambdaの上限を超えて必要な処理が含まれていたため、Fargateも含む構成になりました。 わずか3日でシステムは稼働を開始。開発・運用コストがあまりにも低かったため、クライアントには驚かれました。運用開始から1年が経過しますが、障害は一度もなく、コード修正も即日で対応できています。 事例3:10時間かかっていた遺伝的アルゴリズムを並列分散処理で高速化 PoC中の最適化計算で、遺伝的アルゴリズムの実行時間が長すぎる(10時間!)という課題がありました。アルゴリズムの根本的な改良には時間が足りない状況です。 そこで、データサイエンティストは既存コードと「ざっくりした並列化のイメージ図(Master-Slave方式)」をエンジニアに共有しました。エンジニアはコードを読み込み、即座に並列分散処理を実装。 初版ができた後には、こんな最高な会話もありました。 結果として計算時間は大幅に短縮され、実用化に必要とされた処理速度を余裕で達成。使いやすさも向上。アプリへの移行も非常にスムーズに進みました。 こちらの事例については、別の記事で技術面を詳しく解説しておりますのでぜひご覧ください↓ 10時間かかっていた遺伝的アルゴリズムをAWS Lambdaで高速化 爆速開発を可能にする3つの秘密 このスピードを実現できるのは、大きく3つの要因があると考えています。 1つ目は「ワンチームで動く」こと。 データサイエンティストとエンジニアが同じリポジトリでプルリクを送り合い、Daily MTGも合同で行うため、仕様変更や方針転換が即座に反映されます。 Insight Edgeのコアバリューに「みんなでやる」というものがありますが、まさにそのバリューに則って動いています。 2つ目は「お互いの領域を知る」こと。 データサイエンティストはAWSやGCPの資格を所持しているメンバーも多く、アーキテクチャ図もある程度は描けます。一方でエンジニアはAIやアルゴリズムに理解があり、遺伝的アルゴリズムのコードもすぐに理解し改良できました。この「浅くても相手の言葉がわかる」関係が、やり取りを高速化します。 3つ目は「まず使ってもらう」こと。 完璧なモデルを机上で作り上げるよりも、現場に投入して改善プロセスを回す方が結果的に現場にとって良いモデルができる。小さく作って早く届け、そこから進化させるという考え方です。 最後に Insight Edgeでは、 データサイエンティストが作ったモデルを、エンジニアの力で爆速で現場投入 できる環境があります。 このような爆速開発チームで一緒に価値を届けてみたい方、データサイエンスとエンジニアリングの境界を越えて働きたい方は、ぜひ 採用ページ をご覧ください!
こんにちは!アジャイル開発チームの齊藤です!近年、ブラウザ操作エージェントの技術革新が目覚ましく、一般ユーザーでも手軽に利用できるようになってきました。ChatGPT AgentがPlusユーザーに開放されるなど、人の代わりにAIがWebサイトを自動操作する環境が整いつつあります。 これらの技術は業務効率化や自動化においてメリットをもたらしますが、一方でWebサイト運営者にとっては新たなセキュリティリスクとなる可能性があります。本記事では、最新のブラウザ操作エージェントの動向や想定されるリスクを整理するとともに、無料のBot対策サービスを用いた検証結果もあわせて紹介します。 ブラウザ操作エージェントの最新動向 ChatGPT Agent Playwright MCP Comet ブラウザ操作エージェントのリスクと対策 想定されるリスク 対策の動向 ブラウザ操作エージェント対策の比較検証 Bot検知サービスの仕組み 実験シナリオと環境 実験結果 考察 最後に ブラウザ操作エージェントの最新動向 ここでは、代表的なブラウザ操作エージェントのツールやサービスを紹介します。いずれも自然言語による指示でWeb操作を自動化できる点は共通していますが、動作環境や特徴には違いがあります。 ChatGPT Agent 2025年に入り、OpenAIはChatGPT AgentをPlusユーザーにも開放しました。これにより月額20ドルという比較的低いハードルで、誰でもブラウザ操作エージェントを利用できるようになりました。 ChatGPT Agentは自然言語の指示に従って仮想環境上からWebサイトを自動操作し、フォームの入力、ボタンのクリック、情報の収集などを実行できます。例えば「不動産サイトで△△駅徒歩10分以内、家賃10万円以下の1K物件を検索してリストアップして」や「旅行予約サイトで来月の大阪行きの格安航空券を調べて」といった指示で、実際にAIがブラウザを操作してユーザへのリストアップまで行います。 Playwright MCP Playwright MCPと生成AIを連携させることで、チャットベースでローカル環境からAIによりブラウザを操作できます。 例えば以下のようなプロンプトを入力すると、指示に従ってページを読み解き、回答が得られるまでブラウザ操作してくれます。 〇〇で『△△駅周辺の和食レストラン』を検索し、 評価3.5以上のある店舗を3件リストアップしてください また口コミ評価をまとめてください。 Comet Comet(PerplexityのAIブラウザ)は、AIを組み込んだブラウザです。ChatGPT AgentやPlaywright MCPのように新規でブラウザを立ち上げるのではなく、閲覧中のWebサイトを直接解析・操作できる点が特徴です。ユーザーが事前にログインしていれば、その状態を維持したままAIによる操作が可能なため、たとえばX(旧Twitter)への自動投稿も容易です。 ただし、この仕組みは便利である反面、セキュリティ上のリスクも伴います。特に「プロンプトインジェクション」により意図しない操作や情報漏洩が発生する可能性があるため、十分な注意が必要です(後述)。 ブラウザ操作エージェントのリスクと対策 想定されるリスク ブラウザ操作エージェントの普及に伴い、以下のようなリスクが想定されます。 人間と機械の判別が困難になりセキュリティ検知を回避される :従来のBot対策は、マウスの軌跡や操作パターンの機械的な規則性を検知していましたが、LLMベースのエージェントは人間らしい自然な操作パターンを生成するため、従来の検知手法では識別が困難になる可能性があります。 ブルートフォース攻撃やスパム配信の自動化 :ログインフォームに対する総当たり攻撃や、問い合わせフォームを悪用した大量スパム送信が、より人間らしい操作パターンで実行される可能性があります。 限定商品の転売目的での自動大量購入 :ECサイトで限定商品が発売される際に、人間と見分けがつかない操作で購入を繰り返し、転売目的で商品を独占する行為が懸念されます。 サーバー負荷増大やサービス妨害 :大量のエージェントによる同時アクセスにより、サーバーに過負荷をかけてサービス停止を引き起こすDDoS攻撃の可能性があります。 プロンプトインジェクションを悪用した企業システムへの不正操作 悪意あるウェブページに仕込まれた「隠し命令(プロンプト)」をブラウザ操作エージェントが実行してしまうことで、企業のシステムやサービスが意図しない挙動を取らされるリスクがあります。たとえば、問い合わせ窓口からの不正データ取得や、認証プロセスの迂回、さらにはアカウント情報や決済情報の流出などが発生する可能性があります。 Example of Prompt Injection Risk プロンプトインジェクションの概略図 対策の動向 現時点では、ブラウザ操作エージェントによる具体的な被害報告は調査した範囲では見つかっていません。ただし技術の進化に伴い今後増加する可能性があり、各社で対策が始まっています。 例えば、 hCaptchaはブラウザ操作エージェントに対する公式声明 を発表し、AIエージェントの発達に対応するための技術開発を進めています。また、 ChatGPT AgentがAmazonにアクセスできなくなる といった事例も報告されており、大手Webサイトでは既に対策が始まっていることが伺えます。 ブラウザ操作エージェント対策の比較検証 Bot検知サービスの仕組み Bot検知サービスは、Webサイトにアクセスしてくるトラフィックが人間によるものか、Botによる自動化されたアクセスかを判別するサービスです。 一般的なBot検知サービスは以下の要素を総合的に分析してBot判定します。 ブラウザフィンガープリンティング : User-Agent、画面解像度、プラグイン情報等 行動分析 : マウスの動き、キーボード入力パターン、ページ遷移の流れ IPアドレス分析 : アクセス元IPの評判や地理的情報 機械学習モデル : 過去の学習データに基づくパターン認識 判定方式は様々ありますが、今回は2つの方式をご紹介します。 ① スコアベース判定(例:reCAPTCHA Enterprise) 正常なユーザーの場合 画面上には何も表示されない バックグラウンドでスコア(0.0〜1.0、1に近いほど人間に近い)が計算される スコア0.7以上など高スコアの場合、そのまま処理を続行 Botの場合 画面上には何も表示されない 低スコア(0.1など)が返される サイト運営者がスコアに基づいて以下のアクションを選択 アクセスを拒否 追加認証を要求 ログに記録して監視 特徴 ユーザー体験を損なわない サイト運営者が閾値を調整可能 グレーゾーンの扱いが課題 ② チャレンジベース判定(例:Cloudflare Turnstile) 正常なユーザーの場合 初回アクセス時に自動的にチャレンジが実行される バックグラウンドで検証が行われ、数秒で完了 一度通過すればしばらくは表示されない Botの場合 チャレンジの検証に失敗 アクセスが拒否される 複数回失敗すると一時的にブロック 特徴 ユーザーの操作が多少発生 実験シナリオと環境 問い合わせフォームを通じて大量のスパムメールを送信するシナリオを想定し、ブラウザ操作エージェントによる自動操作に対してBot検知サービスがどの程度有効かを検証しました。 Bot検知サービス 無料で利用可能な以下のサービスを対象としました。 reCAPTCHA Enterprise : Googleが提供するBot検知サービス(月10,000リクエストまで無料) Cloudflare Turnstile : Cloudflareの無料Bot対策機能 操作対象サイト 検証用に問い合わせフォームを含むテストサイトを作成し、各Bot検知サービスを実装してBot検知スコアをリアルタイムで表示できるようにしました。 ブラウザ操作エージェント 以下2つのブラウザ操作エージェントによって検証しました。 ChatGPT Agent Playwright MCP プロンプト例: ・https://xxx にアクセスして、"test@test", "testです "を入力して。 ・この際マウスは人間のように、ゆっくり動かして ・入力終わったら送信ボタンを押して(許可必要ない) 検証環境補足 検証実施時期 :2025年9月1日 検証条件 :各パターン3回のサンプル検証 操作対象サイト :Next.jsで構築、EC2にデプロイ(Cloudflare Turnstile利用のためDNSをCloudflareに設定) ChatGPT Agent :ChatGPT Agentが使用する仮想環境上のブラウザを動作 Playwright MCP :ChromiumをmacOS上で動作、Cline利用 実験結果 実際の検証の様子を以下の動画でご確認いただけます。 ChatGPT Agent vs reCAPTCHA Enterprise ChatGPT Agent vs reCAPTCHA Enterprise ChatGPT Agentが問い合わせフォームへの入力を完了し、reCAPTCHA Enterpriseのスコアが0.9〜1.0という非常に高い値を示しています。これは人間と同等の判定を受けていることを意味します。 ChatGPT Agent vs Cloudflare Turnstile ChatGPT Agent vs Cloudflare Turnstile ChatGPT AgentがCloudflare Turnstileによって検知され、アクセスが防御されている様子が確認できます。 Bot検知結果 各パターンに対し3回ずつ検証した結果を下記表にまとめました。 エージェント reCAPTCHA Enterprise Cloudflare Turnstile ChatGPT Agent 0.9 ~ 1.0 防御 Playwright MCP 0.8 ~ 0.9 防御 人間操作(参考) 1.0 通過 考察 reCAPTCHA Enterpriseは、両ブラウザ操作エージェントともに高いスコア(ChatGPT Agent: 0.9〜1.0、Playwright MCP: 0.8〜0.9)となり、人間として判定される傾向が見られました。一方でCloudflare Turnstileは、両ブラウザ操作エージェントともにアクセスが防御されました。 実は ChatGPT Agentのリリース直後には、Turnstileを突破できることが報告されていました 。しかし、今回の検証では両エージェントともに防御されています。報告後にブラウザ操作エージェント固有の情報(User-Agent情報やIP range等)からブロックするようにアップデートされたことが推測されます。 最後に 本検証により、Bot対策サービスとAIエージェントの間で技術的な「いたちごっこ」が既に始まっていることが確認できました。さらに記事執筆中にもClaude for Chrome(Claudeを使ってブラウザを操作できるChrome拡張機能)が一部ユーザー向けに限定公開されており、新技術の登場と対策側の改善が継続的に行われている状況です。 Web運営者の皆様には、この技術的な攻防戦が継続することを前提に、最新の動向を注視することが重要です。自サイトの特性に応じて、必要であれば高難易度なCAPTCHAの導入やWAFなど多層防御による対策を継続的に実施していくことをお勧めします。 以上、ブラウザ操作エージェントの利用や対策、AIエージェントの動向を調査されている方々の参考になれば幸いです。最後までお読みいただき、ありがとうございました。
こんにちは、Insight Edgeデータサイエンティストの中野です。 データサイエンスやLLMのプロジェクトを進めていると、こんな課題にぶつかった経験はありませんか? ラベル付きデータが足りず、最初からモデルを作れない 課題感はあるけど、問題定義が曖昧で進め方がぼんやりする 作ったものが「本当に役に立っているのか」評価できない 私も個人で取り組んだ家計簿分析プロジェクトで、まさにこれらの課題に直面しました。そこで試したのが 「評価駆動開発(Evaluation-Driven Development)」 です。 この記事では、家計簿アプリの明細分類を題材に、評価を起点にプロジェクトを進めた流れと学びを紹介します。 なおこの記事で紹介する方法は、OpenAIの評価駆動型システム設計のノートブック *1 を参考にしています。 なぜ評価駆動が重要なのか 家計簿分類プロジェクトを評価駆動で進めてみる 1. 問題定義 2. データ整備 3. 家計簿明細を分類する処理の実装 4. 評価メトリクスの定義と計算 Ground Truthデータセットの作成 定義と計算 5. 業務インパクトの定義と計算 6.継続的な改善 まとめ 参考文献 なぜ評価駆動が重要なのか 通常のデータサイエンスは「データ収集 → モデル構築 → 精度向上」という流れで進められます。 しかしこのアプローチには、初期段階で以下の課題があります。 課題1:ラベル付きデータ不足 : プロジェクトの初期は、データが揃っていないことが多いです。特にLLMを活用したプロジェクトでは、例えば口コミデータや動画データなど、大量に貯まっているものの、特にラベルがついていない状態で始まることも少なくありません。 課題2:問題理解の不足 : なんとなく課題感や困っていることはあっても、具体的な問題定義が不十分なまま進むことは多いです。課題感はあるものの定義が曖昧で、進めるうちに「そもそも解くべき問題が違った」となることも。 結果として「何を改善すればいいか」「今の性能が十分なのか」が分からないまま、なんとなく進んでしまいます。 そこで有効なのが評価基準を先に置き、開発を進める評価駆動開発です。 評価基準も最初から完璧である必要はありません。「仮のもの」をまず定めて進めることが大切です。 家計簿分類プロジェクトを評価駆動で進めてみる 評価駆動開発を進めるために、次のステップでプロジェクトを進めます。 問題定義 データ整備 家計簿明細を分類する処理の実装 評価メトリクスの定義と計算 業務インパクトの定義と計算 継続的な改善 1. 問題定義 現在、私はあるアプリを使って、クレジットカードやECサイトの購買履歴を記録しています。 以下の画像のような粒度で明細が記録されていますが、とくに確認や分析はしていません。 また、なんとなく外食やコンビニでの買い物が多く、削減したいと考えていますが、とくになんの分析もしていません。 今回の目的は、家計簿の明細にカテゴリラベルをつけて、外食などの比率を可視化し節約につなげることとします。 重要なのは外食などの浪費を可視化して節約の意識を高めることがゴールであって、カテゴリ分類精度を極限まで高めることではありません。 そのため、まずAIでラベル付けさせますが、分類が難しい明細は「不明」として人間にラベルを付けさせることとします。 金額が小さいものは無理にラベル付け誤分類しても影響が小さいので、間違えていても気にしません。 2. データ整備 データはアプリからCSVをダウンロードします。 実装を進めるにあたり以下のようなデータモデルを定義します。 明細データを TransactionRecord 、分類結果 ExpenseClassification として定義します。 分類結果のデータモデルはLLMの構造化出力のスキーマ指定にも使用します。そのため、 title や description も記載していきます。 *2 from enum import Enum from pydantic import BaseModel, Field class TransactionRecord (BaseModel): """家計簿の取引レコードを表すデータモデル""" date: str = Field(description= "取引日付 (YYYY-MM-DD)" ) amount: int = Field(description= "取引金額(円)" ) description: str = Field(description= "取引内容の説明" ) class ExpenseCategory (StrEnum): """支出のカテゴリ""" FIXED = "固定費" TRANSPORTATION = "交通費" FOOD = "食費" EATING_OUT = "外食" BOOK = "本" HEALTH = "健康美容" CONVENIENCE_STORE = "コンビニ" HOBBY = "趣味娯楽" EQUIPMENT = "日用品" UNKNOWN = "不明" class ExpenseClassification (BaseModel): reasoning: str = Field( title= "思考過程" , description= "支出のカテゴリを決定するための思考過程を記載する。" , ) label: ExpenseCategory | None = Field( title= "支出のカテゴリ" , description= "支出のカテゴリを示す。分類が出来ない場合は不明とすること" , ) 3. 家計簿明細を分類する処理の実装 Geminiを使って、家計簿明細の分類処理を実装します。 先ほど定義した TransactionRecord を入力として、 ExpenseClassification を出力する関数を実装します。 構造化出力にはGeminiの出力制御機能を利用します。 プロンプトはシンプルなものとしています。 最初からプロンプトを工夫しすぎるよりも、まずは動かして評価することを重視しました。 from google import genai from google.genai import types def classify_expense (transaction_record: TransactionRecord) -> ExpenseClassification: prompt = f """ 家計簿の明細が与えられた場合、この明細に以下のルールに基づいてラベルを付与してください。 # ラベル付けのルール 1. 明細の内容にもとづいて、ラベルをつけること 2. 出費が必要経費か、無駄な出費であり、改善するべき対象であるか分類すること 3. ラベル付けの判断が難しいときは、「不明」として人間のエキスパートにレビューを依頼すること # 対象の家計簿明細 {transaction_record} """ .replace( " " , "" ) client = genai.Client() response = client.models.generate_content( model= "gemini-2.5-flash" , contents=prompt, config=types.GenerateContentConfig( response_mime_type= "application/json" , response_schema=ExpenseClassification, thinking_config=types.ThinkingConfig( thinking_budget= 0 ), ), ) result: ExpenseClassification = response.parsed return result 4. 評価メトリクスの定義と計算 先ほど実装した classify_expense 関数の性能を評価します。 最初に、Ground Truthデータセットを用意します。 その後、分類タスクの混同行列を用いて、モデルの性能を確認します。 Ground Truthデータセットの作成 Ground Truthデータセットを作成していきます。 各カテゴリに5件ずつ程度作成しました。 漠然とGround Truthデータを作っていると、人間がラベル付けしやすいものばかりに偏ると感じました。 そのため、今回は意図的に難易度が高い事例も含めるように注意しました。 [ { " TransactionRecord " : { " date ": " 2024-11-30 ", " amount ": 7260 , " description ": " 丸善日本橋店/NFC " , } , " ExpenseClassification ": { " reasoning ": "", " label ": " 本 " , } } , { " TransactionRecord " : { " date ": " 2024-11-30 ", " amount ": 27840 , " description ": " 日本橋髙島屋S.C. " , } , " ExpenseClassification ": { " reasoning ": "", " label ": " 不明 " , } } ] 定義と計算 混同行列を作成して分類精度を確認します。 これを見ると、そこそこ適切に分類されています。 一方で、本来「健康美容」カテゴリの明細がいくつか他のカテゴリに分類されていることがわかります。 しかし、これらの情報だけで、今回のプロジェクトに対して十分な精度かはわかりません。 誤判定や不明の数も許容できるものか?といったことは、プロジェクトの目的に依存します。 間違えている明細の分析と改善方針 以下の今回間違えた、明細の内、不明としなかった項目を整理します。 金額や明細名は一部加工しています。 No. 明細名 金額 正解ラベル 出力ラベル chain of thoughtの出力 1 ABC[美容院名] [地名]/NFC 6000 健康美容 外食 ABC [地名]は飲食店であり、NFC決済であることから、外食費に該当すると判断しました。支出額も比較的高いため、無駄な出費となりうるか検討すべき対象と考えられます。 2 DEF[美容院名] [地名]/NFC 9000 健康美容 日用品 DEFというブランド名から衣料品費と判断しました。衣料品は必需品ではなく、無駄遣いと判断できるため、改善対象として分類しました。 3 〇〇 東京日比谷 10000 外食 趣味娯楽 〇〇 東京日比谷はホテル名であり、宿泊費やそれに伴うサービス利用料と推測される。これは個人の生活スタイルや目的によるが、一般的には「趣味娯楽」または「不明」に分類される。今回は旅行やレジャー目的と判断し、「趣味娯楽」に分類する。 4 タイムズカー 202402ゲツガクキホンリヨウ 880 交通費 固定費 タイムズカーはカーシェアリングサービスであり、利用頻度に応じて料金が発生する。これは月額基本料と推測され、固定費に分類するのが妥当である。 5 メルスプラン月額費用 5060 健康美容 固定費 メルスプランの月額費用は、定期的に発生する医療関連の費用であり、生活に不可欠な経費と判断されるため、「固定費」と分類します。 6 平安伸銅工業 LABRICO DIY収納パーツ 2×4アジャスター強力タイプ オフホワイト 最大使用荷重40kg EXO-1 販売: アマゾンジャパン合同会社) 1200 日用品 趣味娯楽 2x4アジャスターはDIYの材料であり、生活必需品ではないため、趣味娯楽費に分類し、改善の余地がある支出と判断しました。 7 角利産業(Kakuri Sangyo)プロメイト 糸鋸デラックス 焼入刃付 No.7260 販売: アマゾンジャパン合同会社 887 日用品 趣味娯楽 糸鋸デラックスはDIYや工作に使用される工具であり、趣味娯楽費に該当すると判断しました。 8 魚〇〇 大手町店 1200 外食 食費 「魚〇〇 大手町店」での支払いは、食費に該当すると判断しました。具体的な食料品の内容は不明ですが、一般的に飲食店の利用は食費として計上されます。この支出は、毎日の生活に必要な食費の一部であり、無理な節約をする必要はないと考えられます。 No.1,2は美容院名のため」健康美容にラベル付することが正解なのですが、誤って飲食店や洋服店と判断されてしまっています。 金額のインパクトも大きく、定期的に発生するもののため、改善の優先度が高いと考えられます。 人間なら検索すればすぐわかるもののため、検索ツールをグラウンディングし精度改善を図っても良いと思います。 No.3は、ホテルでの食事ですが、宿泊と判断されてしまいました。 金額が高いため対処したいものの、LLMで判断できる範囲を超えているため、不明とするのが妥当かもしれません。 そもそも1回の支出で1万円を超過するケース自体稀なため、金額が大きい中で確信が持てないものは不明とするロジックを追加しても良いと思います。 No.4,5は、カーシェアリングの月額費を交通費、コンタクトレンズの定期支払を健康美容としていますが、誤って固定費と判断されています。 Ground Truth作成時はそれぞれ交通費・健康美容と判断したものの、よくよく考えたら固定費で良い気がしました。 そのため、この2つはGround Truthを修正します。 このように、LLMの判断を人間の判断に一致させる作業の最中に、人間の正解が変わっていく現象は実プロジェクトでもよく起こります。 Criteria Drift *3 と呼ばれたりもします。 対処方法ですが、個人的にはとにかく早く評価サイクルを回して、このような現象があることを把握し、すこしづつ評価基準を育てていくことが正攻法ではないのかと考えています。 No. 6,7,8は金額も小さく、非定期なものであるため、優先度は低くして良いと考えます。 5. 業務インパクトの定義と計算 先ほどは、ひとつひとつ人の目で間違いを分析し改善の方針を決めました。 しかし、今後データ量や制約も増えてくる中で、ひとつひとつ確認するのは非効率です。 そこで、LLMシステムの改善の優先度を決めるため、LLMの評価指標を業務へのインパクトに変換する関数を定義します。 実務の場合はLLMアプリの評価指標を、改善金額や削減時間などのビジネスインパクトに変換できると良いのですが、 今回はそこまで出来ないので、以下のような制約を持つ悪影響度合( negative_impact )を定義します。 制約1: 間違えた金額が大きいほど、分析作業への悪影響度合は大きい 制約2: 不明とラベルを付けた場合よりも、ラベルを付け間違えた方が浪費分析への悪影響度合は大きい(10倍くらい) def calculate_negtive_impact (evaluation_records: list [EvaluationRecord]) -> float : negative_impact = 0.0 for rec in evaluation_records: if rec.correct_classification.label != rec.predicted_classification.label: if rec.predicted_classification.label == ExpenseCategory.UNKNOWN: negative_impact += rec.transaction_record.amount / 10 else : negative_impact += rec.transaction_record.amount return negative_impact この関数で、分類精度を浪費分析への悪影響度合を計算したところ「69344」でした。 この指標をもとに、効率的にシステムを改善していきます。 6.継続的な改善 先ほどの改善方針を踏まえて、プロンプトを以下のように改善します。 prompt = f """ 家計簿の明細が与えられた場合、この明細に以下のルールに基づいてラベルを付与してください。 # ラベル付けのルール 1. 明細の内容にもとづいて、ラベルをつけること 2. 出費が必要経費か、無駄な出費であり、改善するべき対象であるか分類すること 3. ラベル付けの判断が難しいときは、「不明」として人間のエキスパートにレビューを依頼すること 4. 飲食店名や美容院名などマイナーで固有名詞が不明な場合は「不明」とする 5. ホテル名称などで飲食か宿泊か判断できない場合は「不明」とする 6. 大手コンビニチェーン(セブンイレブン、ファミリーマート、ローソンなど)の場合は「コンビニ」とする 7. 毎月固定の金額(家賃、保険、サブスクなど)の場合は「固定費」とする # 対象の家計簿明細 {transaction_record} """ 再度分類した所、混同行列は以下のようになりました。 negative_impactは「19710.6」です。改善できていそうですね。 ただし、なんでもかんでも不明とラベルづけしているため、 本当にこの指標で浪費節約意識を高める作業が効率的になっていくのかは更に運用をしていかないとわかりません。 今後はよりデータを増やしたり、意思決定につなげる中で、評価指標やGround Truthデータ、業務インパクトの定義を育てていくことが必要と思います。 まとめ この記事では「評価駆動開発」という考え方を、家計簿分類プロジェクトを通じて紹介しました。 LLMプロジェクトだと検索や要約といった機能を多く組み合わせて構築すること、対象となるデータソースが多岐にわたりフォーマットも異なることが多いです。 そのため、とりあえず動かせた後、どこから改善していけばよいかわからないことが多いです。 今回の様に業務インパクトを定義して評価を先に置くことで この工程は精度がわるくても、現時点では問題なさそう このデータソースは精度が悪くても、数も少なく人手による修正作業も容易なので改修の優先度が低い といった判断ができ、効率的に開発を進められるのではないかと思いました。 参考文献 *1 : OpenAIの評価駆動型システム設計のノートブック *2 : GeminiのStructured outputでレスポンスの型を矯正するためのTips 7選 *3 : Who Validates the Validators? Aligning LLM-Assisted Evaluation of LLM Outputs with Human Preferences
はじめまして。Insight Edgeで営業を担当している塩見と申します。 普段はBtoBの領域で活動していますが、今回は私が個人で取り組んでいるソーシャルビジネス事業について、その立ち上げプロセスと生成AIの活用術を一つのテックブログとしてまとめたいと思います。 この活動は、社会貢献を目的としたビジネス、いわゆるソーシャルビジネスです。きっかけは、2024年の1月から4月にかけて参加した、ボーダレス・ジャパン社が運営する「ボーダレスアカデミー」でした。ここでは社会課題を解決するための事業プランを練り上げ、多くの起業家の方々と壁打ちを重ねながら、アイデアを具体化する訓練を積みました。 現在、そのプランを実行に移すフェーズにあり、その過程でバイブコーディングや各種生成AIツールを駆使しています。ランディングページ(LP)やプロモーション動画といった複数のクリエイティブを制作しましたが、これらはすべて私一人で、1〜2ヶ月という短期間で完成させました。本記事では、その具体的な取り組みをご紹介します。 社会課題としての「AIデバイド」と事業アイデアの着想 デジタルデバイドの再来、広がる生成AIの利用格差 「声で学ぶAI教室」- 誰もが生成AIの恩恵を受けられる社会へ アイデアを形に:生成AIとバイブコーディングで創るLPとチラシ 音声対話からLP制作へ - Geminiとbolt.new活用 外部サービス連携 Canvaと生成AIレビューで作るチラシデザイン 知ってもらうための仕組みづくり:広告、情報発信の効率化、そしてプレスリリース 学びとしての初挑戦 - オンライン・オフラインでの広告運用 声で思考をストックし、発信する - Discord BotとObsidianによる知的生産術 プレスリリース 「さっさと失敗する」今後の展望 失敗から学ぶ - アジャイルな挑戦を支えるマインドセット おわりに 社会課題としての「AIデバイド」と事業アイデアの着想 デジタルデバイドの再来、広がる生成AIの利用格差 昨今、ChatGPTをはじめとする生成AIの進化と活用が急速に進んでいます。しかし、その恩恵を享受できているのは、まだ一部の人々に限られているのが現状です。多くの記事で指摘されているように、日本全体で見ると生成AIの活用は進んでおらず、さらに「使える人」と「使えない人」の格差、いわゆる「AIデバイド」は深刻化しつつあります。 かつてスマートフォンが登場した際、同様に「デジタルデバイド」が社会問題となりました。10年以上の時を経て、今や60代、70代の方々のスマホ保有率も70%〜90%に達し、この問題は徐々に解消されつつあります。 しかし、物理的なデバイスであるスマホと異なり、生成AIは実体が見えにくいため、この格差はより一層、加速度的に広がっていくのではないかという強い危機感を持っています。このままでは、生成AIによって助かるはずの人が助からなかったり、国全体の生産性が伸び悩んだりする未来に繋がりかねません。 「声で学ぶAI教室」- 誰もが生成AIの恩恵を受けられる社会へ そこで私が着想したのが、「声で学ぶAI教室」という事業アイデアです。 最近、タイピングすら不要な「声(音声入力)」で生成AIを操作する活用法が注目されています。音声認識の精度が劇的に向上したことと、生成AI側で誤字脱字を吸収してくれることで、声で生成AIと対話し、データ分析やLP作成、悩み相談まで、様々なアウトプットを生み出すことが可能になりました。音声入力の速度はタイピングの2〜4倍程あると言われていますので、情報入力の利便性がとても高いです。 この「声」というインターフェースは、生成AIに馴染みがない方や、パソコンが苦手な方にとって、技術的なハードルを大きく下げてくれます。この親しみやすさを起点に、生成AIの便利さを体験してもらい、一人でも多くの利用者を増やしたい。そんな想いから、地元で少しずつこの教室を始めることにしました。 声で学ぶAI教室( https://voice.datafriends.jp/ ) アイデアを形に:生成AIとバイブコーディングで創るLPとチラシ 事業を伝えるためには、サービスの顔となるランディングページ(LP)と、地域の方々に直接届けるチラシが不可欠です。ここでは、その制作プロセスと生成AIの活用法を解説します。 音声対話からLP制作へ - Geminiとbolt.new活用 今回のLP制作では、バイブコーディングのアプローチを取り、具体的にはbolt.newというツールを活用しました。 制作プロセスは以下の通りです。 要件定義(Geminiとの壁打ち): まず、どのような要素がLPに必要か、Geminiの最新モデルと音声で対話しながらディスカッションを行いました。 プロンプト生成: 壁打ちで固まった要素を元に、bolt.new に入力するための具体的なプロンプトを作成します。 UI生成と改善: 生成されたUIをベースに、細部を改善していきます。 キーメッセージの調整や説明文の修正は、再度生成AIに指示を出したり、自分で直接コードを編集したりして対応。 ヒーロー画像は、ChatGPTの画像生成AIを使って、事業コンセプトに合ったオリジナルの画像を生成し、差し替えています。 規約などの専門知識が薄い部分も、生成AIに相談しながら文章を作成しました。このように生成AIと対話を繰り返すことで、驚くほど迅速にLPの骨格を組み上げることができました。 外部サービス連携 LP単体では完結しない機能は、外部サービスを連携させて実装しました。 授業予約・決済システム 無料で利用できる「Square」を導入し、講座の予約動線を確保。 顧客接点 LINE公式アカウントをゼロから作成し、LPから友だち登録ができるように設定。 信頼性向上 「本当に声で生成AIが使えるの?」という疑問に応えるため、実際に操作している様子のデモ動画をYouTubeで公開し、LPに埋め込み。 最終的には、自身で契約しているレンタルサーバーとドメインに紐づけてLPを公開。一連の作業を通して、サービス紹介ページを個人で手軽に公開できる時代になったことを実感しました。 bolt.new Canvaと生成AIレビューで作るチラシデザイン Webでの展開と同時に、起業家の先輩方からは「地道なチラシ配りが重要」と教わりました。Webに慣れていると非効率に感じがちですが、地域に根差す上では欠かせない活動です。 チラシ制作にはCanvaを活用。豊富なテンプレートからイメージに近いものを選び、キャッチフレーズや構成要素は生成AIと相談しながら詰めていきました。 ある程度形になった段階で、自分だけでは改善点が分からなくなったため、ここでも生成AIレビューを依頼。完成したチラシを画像化してGeminiに読み込ませ、デザインのフィードバックをもらいました。すると、「この要素はもっと前に出した方が良い」「こういう表現の方が響きやすい」といった具体的な改善案が多数得られ、品質を大きく向上させることができました。 Canvaと生成AIレビューで作成したチラシデザイン 知ってもらうための仕組みづくり:広告、情報発信の効率化、そしてプレスリリース LPとチラシという「武器」が完成し、次はいかにしてそれを届けるか、という「認知・集客」のフェーズです。 学びとしての初挑戦 - オンライン・オフラインでの広告運用 今回は、効果測定と学びを得ることを目的に、小規模予算で有料広告に初挑戦しました。 オンライン広告: Facebookアカウントからメタ広告を設定。オンライン広告からLPへ誘導します。 オフライン広告: 印刷サービスの「イロドリ」に依頼し、チラシのポスティング広告を実施します。今回は、生成AIに馴染みが薄いと思われる高齢者の方が多く在住されるエリアをターゲットにしました。 この記事を執筆している段階ではまだ入稿したばかりですが、これらの結果を分析し、今後の活動に活かしていく予定です。 声で思考をストックし、発信する - Discord BotとObsidianによる知的生産術 広告のような有料施策と並行して、お金をかけずに認知を広げる取り組みも重要です。友人・知人へのリファラルや無料体験セミナーに加え、SNSでの情報発信にも取り組んでいました。 しかし、継続的な発信は簡単ではありません。そこで、発信頻度を上げるための仕組みを自作しました。 思考のインプット (音声): Discord上に自作したBotを立ち上げ、メンションを付けて音声で思考をインプットします。 生成AIによる整形・ストック: Botが音声をテキスト化し、生成AIが内容を整形。そのテキストをGoogleドライブに自動でストックします。 ナレッジ管理 (Obsidian): GoogleドライブはナレッジベースアプリのObsidianと連携。過去のメモやアイデアとの関連性をマッピングし、知識を体系的に管理します。 自動投稿 (Twitter): ストックされた内容を、Botが自動でX(Twitter)に投稿します。 この仕組みを導入してから、発信のハードルが劇的に下がり、投稿頻度が高まり、SNS経由でのランディングページ閲覧者数が増えました。なお、こちらのツール開発もバイブコーディングで行っています。 自作したDiscord Bot(音声入力の内容の整形とX投稿を行います) こちらのツールは、AIマンガ家&プログラマー・けいすけさんのnoteを参考に開発を進めました。本機能を持ったDiscord Botを一部無償提供されていますので、気になる方は、けいすけさんのnoteとXを是非ご参照ください。 note.com プレスリリース 最後に、先輩起業家のご紹介もあり、本事業のプレスリリースをPR TIMES様から2025年8月5日(火)に打たせていただくことにしました。プレスリリースは自分とあまり関係のないものと考えていましたが、実際はそんなこともなく、簡単な準備と3万円程の費用で、複数のメディアに対して情報発信を行うことが可能です。なお、PR TIMES様では、創業2年目の会社であれば、無料でプレスリリースを打つスタートアップチャレンジといった、大変有難い支援サービスもあります。 プレスリリース文案 こちらのプレスリリースの文案作成に際しても、生成AIにレビューをお願いしました。文章の読みやすさや簡潔さなど、かなりの駄目出しを受けながら、文章作成を進めました。 また、今回はGemini 1.5 ProとChatGPT-4oの両モデルにレビューをしてもらいましたが、共通の指摘事項として「動画コンテンツを用意すべき」というものがありました。動画コンテンツを自身で作成した経験はありませんでしたが、こちらもチラシと同様にCanvaで制作を進めました。 さらに制作過程で、より高い品質を目指してナレーション音声も追加したいと考えるようになりました。そこで活用したのが、Google AI Studioの音声生成機能(Generate speech)です。台本を読み込ませるだけで、非常に品質の高いナレーション音声をAIに作成してもらいました。 豊富なテンプレートとAIのナレーションのおかげで、動画制作の素人でも、ある程度の品質を持ったプロモーション動画を作成できたかと思います。実際の音声は こちらのYouTube動画 でご確認いただけます。 プロモーション動画の制作 www.youtube.com 「さっさと失敗する」今後の展望 失敗から学ぶ - アジャイルな挑戦を支えるマインドセット このソーシャルビジネスは、6月から10月にかけて「本当に成り立つのか」を検証する期間と位置づけています。 もちろん、初めての個人での挑戦なので、失敗する可能性も大いにあると考えています。私が好きなプロダクトデザイナー・秋田道夫さんの「さっさとやってさっさと失敗してさっさともう一回やる事です」という言葉があります。 さっさとやってさっさと失敗してさっさともう一回やる事です。 — 秋田道夫 (@kotobakatachi) 2023年4月2日 今回の挑戦も、この精神で取り組みを進めています。失敗しても、そこで得た学びが必ず次に繋がると考えています。まずは行動し、そこから得られたフィードバックを元に、素早く次の打ち手を考える。このサイクルを回していきたいです。 おわりに 今回は、私が個人で立ち上げているソーシャルビジネスの事例を通して、生成AIを駆使したアイデアの具体化から認知拡大までのプロセスをご紹介しました。 実は、この記事の執筆も「Aqua Voice」という音声入力ツールを使い、私が話した内容を生成AIにテキスト化してもらう形で行っています。手でタイピングするのに比べ、その速度と手軽さは圧倒的です。 「声と生成AIの組み合わせ」は、まだ一般化されていませんが、今後当たり前のワークスタイルになっていくと思われます。 この活動が、AIデバイドという社会課題に対する一つの解決策の提示となり、また、これから何か新しい挑戦をしようとしている方々の参考になれば幸いです。最後までお読みいただき、ありがとうございました。
init_mathjax = function() { if (window.MathJax) { // MathJax loaded MathJax.Hub.Config({ TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true } }, tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ], displayMath: [ ['$$','$$'], ["\\[","\\]"] ], processEscapes: true, processEnvironments: true }, displayAlign: 'left', CommonHTML: { linebreaks: { automatic: true } } }); MathJax.Hub.Queue(["Typeset", MathJax.Hub]); } } init_mathjax(); こんにちは、4月にDSチームマネージャーになった ヒメネス(Jiménez) です!私の名前のリンクをクリックしたことがある方が分かるかもしれませんが、数理博士です。研究分野は代数的位相幾何学(結び目理論)でした。そんな込み入った数学を勉強した人は、 どのように数学を現場で活かしているのか? について話します。 目次 数学で何を勉強する? 社会で活かす、数学者としてのスキル 論理的思考 抽象化・モデル化 ソフトスキル 算数・計算 数学+社会=DS 問題解決へのアプローチ 手法の理解と応用 実践への即応性 まとめ 数学で何を勉強する? 数学が怖い と思う人が非常に多いです。もしかしたら、あなたもその内の一人です。その怖さはどこから生まれるかを考えると、数学は「難しい」からです。「難しい」の定義は人によって変わるかもしれませんが、その裏にある共通の理由は恐らく「 論理的思考を最大限に活かす 」であるのではないでしょうか? その認識は誤っていません。何をしようにも、論理的に考える必要があります。その必要性は数学の根本的な目的から生まれます。では、 数学の目的 は一体何なのですか? 事象の構造や法則性を探求し、関係性を明らかにすること です。数学の言葉に変換すると、A(仮定)があった時に、なぜB(結果)が成り立つかを様々なツール(命題・定理・公理など)を使って 証明する ことです。 前提の背景が変わったり、ツールが変わったり、証明したい内容が変わったりしますが、共通点は論理です。そして、その論理を徐々に徐々に使います。例えば、「 1の次は? 」に対して、ほとんどの人が「2」と答えますが、「それは、なぜですか?」、「どういう仮定(文脈)の下でですか?」、「『1』といった表記は、そもそも何を表しますか?」などを疑い、そして是か否かを証明するのは数学者の仕事です。 社会で活かす、数学者としてのスキル 論理的思考 上記の例のように、背景や前提(仮定)を明確にして、定義された文脈内で問題を考えるのは数学者の特徴です。これは 直接的にビジネスにも活かせます 。素朴な例で恐縮ですが、以下を一緒に考えてみましょう。 例 :とある人が困っています。問題を聞くと、「ドアがちゃんと閉まらないから」と答えます。 ここで、問題をすぐに解決したい人がいたら、ドアの閉まらないところを見つけ、ちゃんと閉まるまで木材を削ります。これで問題を解決できていますか?客観的に答えると、「ドアがちゃんと閉まらない」問題が確かに解決されています。ただ、それで良いのかというと、もしかしたら一番賢い対応方法ではなかったかもしれません。以下考えられます: ドアには価値があり、削るのは勿体無い。 ドアを入れ替える必要があった場合、他のドアも同じく削る必要がある。 他のドアがあるところに同じ問題が発生し得るのか分からない。 など。 そこで、論理を活かし、「なぜ」を疑うことで原因を探索します。 問題 理由 1 ドアがちゃんと閉まらない → ドアと釘がぶつかる 2 ドアと釘がぶつかる → 釘が出っ張っている 3 釘が出っ張っている → 正しい釘じゃない 4 正しい釘じゃない → 正しい釘の余りはなかった ここまでくるだけで、かなり考えられる原因の範囲を絞りました。さらに続くと、釘がなかった理由はもしかしたら作業者が無くしたからです。もしかするとそもそもドアを組み立てる際に釘の数は十分に付属されていませんでした。様々な理由が考えられますが、「なぜ?」を聞くことだけで原因が少しずつ分かり、対応策もそれに応じて変わってきます。( 参考 :この考え方はトヨタ自動車が代表する「 なぜなぜ分析 」とも言います) 抽象化・モデル化 数学では、「具体」を考えることはほとんどありません 。1, 3, 5, 7, 9...は具体的な数列です。数列を見ると、次の数字を推定できますか?「あ!奇数だ!」と気付いて、「11」と答える人はほとんどだと思います。では、他の奇数を考えてください。13? 71? 1895? 奇数は"見つかりやすい"から思いつきベースで答えられると思います。ただ、これは数学者のアプローチではありません。数学者は 具体を抽象化し、一般的なケースを考える ことが多いです。私も1, 3, 5, 7, 9...を見ると「奇数」として認識しますが、頭の中には「$2n+1$」という表現が現れます。奇数にしてはやりすぎかもしれませんが、以下の例を考えてみてください。 例 :1, 3, 6, 10, 15, 21...という数列があった時に、次の数字を推定できますか?その次は?100番目の数字は? 規則が分かっていると思います。0からスタートして、徐々に+1、+2、+3...で計算していきます。では、21の次の数字は... 28でしょう?その次は... 36でしょう?100番目の数字は... 5050、と自信を持って答え切れますか?奇数の場合と違って、この数列に属する数字はそんなに見つかりやすくないので「思いつき」のアプローチは適用できません。一方で、数学的に規則を求めると、上記の数列は次のように表現できます:$\frac{n\cdot (n+1)}{2}$. これが見せられたら、100番目の数字は$\frac{100\cdot 101}{2}=50\cdot101=5050$と簡単に答えられますね!数学者はこのように 抽象化・モデル化を考える上で具体的な回答を導く ことが多いです。 図1:数学的思考による抽象化とモデル化のイメージ。 問題解決に訓練されていない人は問題から直接解答を出そうとします。一方で、数学者(もしくは問題解決思考を訓練した人)は問題の抽象的な表現を考えた上で、論理的な一般化を構築し、最終的に問題に合った具体的な解答を求めます。 どの具体的な問題に対しても必ずその流れで検討を推進するとは言いませんが、このような整理ができるだけでより慎重で網羅的に課題の解決に挑むことができます。そして、具体的な要件が多少変動しても、一般化問題として既に解いた問題と一致するなら、さらに答えを導くのが速いです。 ソフトスキル 上記以外にも、数学の勉強に伴って様々なソフトスキルを伸ばします。 好奇心 :そもそも好奇心なしで数学を選ぶ人はいないと思います。好奇心は数学者の素になります。「なぜ?」、「知りたい!」が根本的な動機です。問題がある手前、 解決せずにはいられない 気持ちです。謎に対する疑問を解消するまで調べ続けること。この好奇心はそのまま社会問題に対しても展開できます。 粘り強さ・忍耐力 :上記にもつながりますが、問題が目の前にあった時に、解決するまで諦めないことが多いです。問題がどんなに難しくても「気になる」気持ちをスッキリさせるまで頑張ります。時間の問題ではありません。 答えを得るのが最高のスッキリ感 なので、それに向けてひたすら頑張ります。(答えが一般的に知られている場合でも、自力で見つけるまで答えを聞きたくない人もいます(例えば、本稿の著者)) 批判的思考・判断力 :自分が持っている前提(仮定)とツール(定理)で結論が証明できるかを見極める力。これはとても大事です。証明できると思った時に、証明しようとします。証明に至らない場合、前提に問題があるか、結論に問題があるか、ツールが足りているかなど確認します。それでも証明ができない場合、 証明できないことを証明します 。これの繰り返しによって、少しずつ文脈を考慮した判断力が身につきます。 補足1 :「証明できない」ことを証明するのに、一つの反例を見つければ十分です。 補足2 :幸せなことに、数学には「意見」は存在しません。事実は事実です。主張を複数の方法で証明できますが、成り立つなら成り立つ、成り立たないなら成り立たない。 算数・計算 多くの人が、数学者は数字を常に扱っているので算数・計算が得意だと思われます。これは大きな間違いです。 まず、数学者は、数字(一般人が考える「数字」)をほぼほぼ使いません。代わりに、数値を表す式を使います。例えば、変数( $x$、$y$ )、係数( $e$、$\pi$ )、多項式( $5x$$^2-x+2 $ )、数式( $ P(x|x>y) $ )、など。数字 そのもの で計算することはほとんどありません。数学の中で唯一数字を見ることが多い分野は 数論 です。それでも、数論で数字の性質を勉強し、純粋に計算・算数をすることはほとんどありません。 そして、数学に必要な能力(論理力)と、算数に必要な能力(計算力)は同じではないため、人によって得意・不得意はまちまちです。例えば、私は数学者で、N次元の複素空間でのベクトルの回転や移動は簡単にできる一方、「127-38=?」と聞かれると答えが出てくるまで日が暮れちゃいます。逆に、算数が得意で数学できない人も山ほどいます。 数学+社会=DS 数学者が社会で技術を活かす最も自然な道はデータサイエンスです。プログラミングさえ習得すれば、論理的思考や抽象化力を活かして、ビジネス課題の問題解決にすぐ着手できます。 問題解決へのアプローチ 数学者は、曖昧な課題を明確な要件に分解し、論理的に解決策を導きます。データサイエンスでも、課題の分解・抽象化・モデル化が重要であり、数学的思考がそのまま役立ちます。また、数学で「証明できないことを証明する」ように、現場でもデータや状況を確認し、実現不可能な点や不足している要素を論理的に明らかにすることが重要です。 手法の理解と応用 DSに必要な統計や機械学習などの手法も、数学的な背景を持つことで理解しやすくなり、なぜその手法が有効なのかまで比較的深く把握しやすいです。DSを含め、すべての科学の根底には数学があるため、数学の構成を理解している人は、他分野の主張やテクニックの確からしさを見極めるのも得意です。新しい技術や手法も、理論から応用までスムーズに取り入れることができます。 実践への即応性 数学者は、プログラミングスキルがあれば、データ分析やモデル構築といった実践的な業務にも迅速に対応できます。たとえ既存のツールやアルゴリズムで解決できない課題があっても、自ら新しい手法を考案したり、既存の理論を応用して独自のソリューションを開発することに取り組むタイプです。また、課題に対して柔軟にアプローチを変えたり、仮説検証を繰り返しながら最適な解決策を模索する力も強みです。こうした即応性は、変化の激しいビジネス現場や新しい技術が次々と登場するデータサイエンス分野で特に重宝されます。 まとめ 数学を学んだからといって、誰もが最強のデータサイエンティストになれるわけではありません。しかし、数学的思考を身につけていることで、物事の本質を素早く捉えたり、論理的に検証する力が他の人よりも高い傾向があります。 とはいえ、ビジネスの現場では人と協力しながら成果を出すことが求められます。どれだけ論理力や抽象化力があっても、コミュニケーションやチームワークが欠けていては、良いデータサイエンティストとは言えません。 結局のところ、社会では多様な能力が必要とされます。 自分の強みを活かしつつ、弱みは周囲と補い合うことで、個人も組織もより大きな成果を生み出せる のです。数学的思考もその一つの武器として、バランスよく活用していきたいと思います。 あっ、「89」だ!😅
はじめに なぜ展示会に出たのか 準備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サービスの進化が非常に速いペースで進む中ではありますが、本記事で扱ったような内容も、現場に寄り添った提供方法の一案として検討していければと思います。