TECH PLAY

株式会社Insight Edge

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

176

本記事でわかること はじめに 背景・課題 目的 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サービスの進化が非常に速いペースで進む中ではありますが、本記事で扱ったような内容も、現場に寄り添った提供方法の一案として検討していければと思います。
アバター
こんにちは、Insight EdgeのLead Engineerの日下です。 今回は、DEAPライブラリを利用した遺伝的アルゴリズムをAWS Lambdaで分散並列実行した話を紹介しようと思います。 目次 目次 背景と課題 並列化の方法の検討 どこを並列化するか? どのように並列化するか? 実装の方針 呼び出し側コード Lambda側コード その他 Lambdaを呼び出すためのDEAPへのmap実装 呼び出し側コード Lambda側コード 今回の実装の工夫ポイント 改善の評価 まとめ 前提 クラウド基盤: AWS 言語: Python ライブラリ: DEAP 背景と課題 ある案件で、 遺伝的アルゴリズム (以下、GA)を用いた最適化処理により業務改善の実証実験をしていたところ、性能に課題があるということでデータサイエンティストチームから相談を受けました。 当該処理は、EC2 (r7g.4xlarge: 16CPU 128GiBメモリ) で 10.8時間 を要しており、以下の問題点がありました。 検証が当日中に完了せず、試行錯誤のサイクルに時間がかかっていた。 運用開始後のエンドユーザの利便性や利用価値の低下も懸念された。 性能改善のアプローチは、大きく分けてアプリ観点とインフラ観点の2つが考えられます。 アプリ観点: アルゴリズムの見直しや、使用言語の変更 インフラ観点: ハードウェアのスケールアップや、並列化によるスケールアウト アプリ観点の改善はデータサイエンティスト側で継続検討することとし、私はインフラ観点で即効性のある改善を目指しました。 また、PoC段階ではEC2を利用していたものの、運用リリースを見据えてLambdaやFargateなどのサーバーレス技術の利用を想定し、ハードウェアの性能向上ではなく分散処理による並列化を目指す方針としました。 並列化の方法の検討 どこを並列化するか? GAには、「1世代内でN個の個体に対する評価関数ループ」と、それを「世代数G回分繰り返すループ」の2重のループがあります。 このうち、後者のG回分の「世代ループ」は前世代の結果に依存して次世代の処理をするため、容易に並列化できません。 一方で、前者の「世代内の個体評価ループ」は各個体の評価は相互依存せず実行できるため、並列化が可能です。 よって、並列化の対象は「世代内の個体評価ループ」としました。 どのように並列化するか? 個体評価ループは1世代あたりの実行時間が短く、[分散処理]→[結果を集約して比較]→[分散処理]→[結果を集約して比較]のループを世代数分繰り返す必要があるため、できるだけオーバーヘッドの少ない方法で並列化する必要があります。 以下の検討ポイントから、AWS Lambdaを分散処理に利用することとしました。 コスト効率 : 処理の実行頻度が高くないため、インフラ維持費の高い常時稼働型サービスは不適 軽量処理への適性 : 並列化対象の「個体評価」処理は軽量なため、呼び出しのオーバーヘッドが少ないLambda (FaaS)を採用 実装の方針 Lambdaを利用した分散処理の実装にあたっては、以下の方針で進めました。 呼び出し側コード 最適化タスク全体の入力パラメタをpickle化し、タスクID(ULIDで一意採番)をキーにしてS3に保存 呼び出し先のLambda関数やS3保存先情報を含んだmap関数を動的に作成し、DEAPに渡す map関数内でLambdaを並列実行し、結果を配列にして返す Lambda側コード evaluate関数をLambdaに対応させる形で実装 Lambda実行時のevent引数で、タスクID、タスク入力値のS3保存先、評価対象の個体リストを指定 タスク入力条件をS3から取得する処理をキャッシュすることで、ウォームスタート時はS3非経由で実行 その他 S3のライフサイクルルールを利用して、アップロードしたpickleは一定期間後に自動削除 実装イメージを図示すると以下のようになります。 実装の実行イメージ Lambdaを呼び出すためのDEAPへのmap実装 呼び出し側コード lambda_map.py import json from concurrent.futures import ThreadPoolExecutor import pickle import boto3 from ulid import ULID from botocore.config import Config boto3_config = Config(max_pool_connections= 100 ) lambda_client = boto3.client( "lambda" , config=boto3_config) s3_client = boto3.client( "s3" ) def prepare_task_params (*, bucket_name: str , prefix: str , input_data: dict ): """ タスクの入力値をS3にアップロードし、Lambda関数に渡すためのパラメータを返す。 """ task_id = f "T{str(ULID())}" # input_dataをpickle化してS3にアップロード input_data_key = f "{prefix}/{task_id}_input.pkl" input_data_bucket = bucket_name s3_client.put_object( Body=pickle.dumps(input_data), Bucket=input_data_bucket, Key=input_data_key, Tagging= "AutoDelete=true" ) task_params = { "task_id" : task_id, "bucket" : input_data_bucket, "key" : input_data_key, } return task_params def call_lambda_for_chunk (lambda_function_name: str , chunk: list , task_params: dict ): """ 1チャンク分の個体配列とタスクパラメータをevent引数にしてLambdaを実行。 """ event_payload = { "individuals" : chunk, "task_params" : task_params, } payload = json.dumps(event_payload) response = lambda_client.invoke( FunctionName=lambda_function_name, InvocationType= "RequestResponse" , Payload=payload, ) result_payload = response[ "Payload" ].read() response = json.loads(result_payload) fitness_list = [ tuple (fitness) for fitness in response[ "body" ][ "results" ]] return fitness_list def create_lambda_map_func (lambda_function_name: str , *, chunk_size= 20 , task_params: dict ): """ チャンクサイズを指定して、AWS Lambda で個体リストを評価するmap関数を作成する。 ・個体リストをチャンクに分割し、AWS Lambda で評価するためのラッパー関数を返す。 ・この関数を DEAP の toolbox に登録して、map 関数として利用する。 """ # 独自のmap関数を定義。ただし、写像処理はLambda側で処理されるため、func引数は受け取るだけで実際には使用しない。 def lambda_map (func, iterable): items = list (iterable) # チャンクに分割(例: 1800 個体を 18 個ずつ → 100 チャンク) chunks = [items[i : i + chunk_size] for i in range ( 0 , len (items), chunk_size)] # チャンク数分の結果リストを初期化 chunk_results = [ None ] * len (chunks) # 各チャンクを非同期でLambda関数に送信し、結果を取得する with ThreadPoolExecutor(max_workers= 100 ) as executor: futures = [ (executor.submit(call_lambda_for_chunk, lambda_function_name, chunk, task_params), idx) for idx, chunk in enumerate (chunks) ] # 分散処理の結果を待ち合わせ、チャンク別の結果を配列に格納 for future, idx in futures: chunk_results[idx] = future.result() # チャンク別の結果をflatなリストに戻す results = [] for chunk_result in chunk_results: results.extend(chunk_result) return results return lambda_map DEAPのmap関数を置き換えるコード from lambda_map import prepare_task_params, create_lambda_map_func def execute_task (task_input: dict ): task_params = prepare_task_params( input_data=task_input, bucket_name= "<your-bucket-name>" , prefix= "intermediate/dev" , ) map_func = create_lambda_map_func( "ga-evaluate-function-name" , chunk_size= 10 , task_params=task_params) # DEAPのToolboxにmap関数を登録 toolbox = base.Toolbox() toolbox.register( "map" , map_func) # ...後略 その他のDEAP設定や遺伝的アルゴリズムの実装は省略 Lambda側コード lambda_evaluate.py import pickle from functools import lru_cache import boto3 s3 = boto3.client( "s3" ) @ lru_cache (maxsize= 15 ) def load_task_input (bucket, key): """ S3からタスク入力データをロードする。 キャッシュを利用して、同じタスク入力データの再取得を高速化する。 """ s3_res = s3.get_object(Bucket=bucket, Key=key) task_input = pickle.loads(s3_res[ "Body" ].read()) return task_input def evaluate_func (individual, task_input_data): """ 個体の評価関数。個体とタスク入力データを受け取り、適応度を計算して返す。 """ # ここに個体の評価ロジックを実装 # 例: 適応度を計算して返す fitness = sum (individual) # 仮の評価ロジック return (fitness,) def handler (event, context): """ Lambda関数のエントリポイント。 event引数には、個体リストとタスクパラメータが含まれる。 """ individuals = event[ "individuals" ] task_params = event[ "task_params" ] # タスク入力データをS3からロード task_input_data = load_task_input(task_params[ "bucket" ], task_params[ "key" ]) # 各個体に対して評価関数を適用 results = [evaluate_func(ind, task_input_data) for ind in individuals] # 結果を返す return { "statusCode" : 200 , "body" : { "results" : results}} 今回の実装の工夫ポイント 必要な処理を分析ロジックから切り離してブラックボックス化することで、既存コードへの影響を最小化。 map関数に落とし込んでいるので、配列から配列に写像(map)する処理を容易に置き換え可能。 フルマネージド&利用従量課金サービスのみに依存することで、手軽に導入可能&運用コスト抑制。 いつでも使える状態を維持しつつ、使わなければほぼ無料でインフラを維持可能。 改善の評価 実際にこの実装を適用して実行時間を測定したところ、以下のような性能改善が得られました。 実行時間の比較 : * 従来のEC2のみ: 10.8時間 * EC2 + Lambda: 2.5時間 この時間短縮により、以下の改善効果が期待できます。 * テスト・検証サイクル高速化 :これまで1日1回しかできなかった精度検証が、業務時間内で複数回実行することが可能になり、評価関数やハイパーパラメータの探索効率が向上。 * 業務効率向上 :アルゴリズムを実用化したときに結果が早く得られるため、エンドユーザの利便性や利用価値が向上。 コストはEC2のみ約 11USD からFargate+Lambdaで約 16USD へ増加しましたが、時間短縮のメリットと、EC2で発生していたアイドル時の無駄なコストがない点を考慮し、許容範囲内と判断しました。 注意点 Lambdaへの大量なアクセスがあるため、ネットワーク速度の影響を受けやすいことに注意してください。 今回の性能検証でも、同一リージョンのEC2からは1世代あたり約0.6秒だったのに対し、 ローカルPCからインターネット経由で呼び出した場合は1世代あたり約3秒かかり、速度改善効果が得られませんでした。 まとめ 遺伝的アルゴリズムの個体評価処理をAWS Lambdaで分散並列化し、以下の改善効果がありました。 GA計算時間を 10.8時間から2.5時間へ短縮 。 DEAPのmap関数置き換えで 既存コード影響を最小化 。 サーバーレス採用で インフラ維持コスト抑制とスケーラビリティ確保 。 今後のプロダクト化にあたっては、アプリ面での最適化も検討し、さらなる性能向上を目指します。
アバター
目次 目次 背景 因果推論とLLM 因果推論 大規模言語モデル (LLM) LLM × 因果推論に関する先行研究 LLMは本当に因果関係を理解しているのか 相関から因果を推論する難しさ:Corr2Causeベンチマーク LLMの因果推論における落とし穴:時系列と反事実の課題 因果推論における「グラフ」と「順序」の重要性 LLMと因果グラフを統合 どのような使い方が良さそうか 今後の展望 終わりに 背景  データサイエンスチームの五十嵐です。本記事ではLLM×因果推論について最新論文を調査した内容をもとに考察します。  近年、大規模言語モデル(LLM)は自然言語処理の分野で目覚ましい進歩を遂げ、多岐にわたるタスクで人間のようなパフォーマンスを示すようになりました。しかし、これらのモデルが「因果推論」、すなわち事象間の原因と結果の関係を正確に理解し、推論する能力を持つかについては、まだ多くの議論と研究が重ねられています。相関関係から因果関係を導き出すことは、科学的発見、意思決定、そしてAIの信頼性を高める上で不可欠です。本記事では、LLMと因果推論に焦点を当て、最新の研究論文を基にLLMの因果推論能力の現状、課題、そして今後の展望について考察します。 因果推論とLLM  ここでは、因果推論とLLMそれぞれについて簡単ではありますが説明します。 因果推論  因果推論とは、ある事象が別の事象を引き起こす、または影響を与えるという原因と結果の関係を特定し、その効果の大きさを定量的に評価する統計的・数学的な手法です。単なる相関関係とは異なり、「なぜ」という問いに答えることを目指します。例えば、「広告費を増やした結果、売上が伸びたのは本当に広告の効果か?」といった問いに答えるために用いられます。因果推論は、政策立案、医療、経済学、ビジネス戦略など、多岐にわたる分野で重要な役割を果たします。 大規模言語モデル (LLM)  大規模言語モデル(LLM)は、膨大なテキストデータで訓練された深層学習モデルであり、人間のような自然言語を理解し、生成する能力を持っています。質問応答、文章要約、翻訳、コード生成など、多様なタスクに対応できます。その基盤は、確率的な単語の連なりを予測することで、既存の知識やパターンを学習し、新しいテキストを生成する能力にあります。 LLM × 因果推論に関する先行研究  LLMが因果推論のタスクにおいてどの程度の能力を発揮するかについては、複数の研究でその可能性と課題が探られていますので、いくつか紹介します。 LLMは本当に因果関係を理解しているのか  Microsoft Researchなどが発表した論文 「Causal Reasoning and Large Language Models: Opening a New Frontier for Causality」 (Microsoft Research、2024年8月)について説明します。  この研究は、LLMが因果推論タスクにおいて人間が行うような推論能力を持つ可能性を「行動学的」に検証しています 。彼らは、従来の因果推論アルゴリズムがデータ間の統計的関連性(共分散)に基づいて因果関係を探索するのに対し、LLMは変数名などの「メタデータ」や自然言語のコンテキストから因果関係を推論する「知識ベースの因果グラフ生成」という、より人間らしいアプローチを取れることを発見しました。  本研究では、現実世界の因果タスクに取り組む際、人々が論理ベースと共分散ベースの因果推論を戦略的に切り替えながら、問いを立て、繰り返し検討し、前提と含意を検証するというプロセスに着目しています。LLMは、このプロセスのあらゆるステップを自動化または支援し、共分散ベースの因果推論と論理ベースの因果推論の間をシームレスに移行できる可能性を秘めていると指摘されています(図1)。 図1  具体的な実験では、例えば「Tubingen因果効果ペアデータセット」(図2)を用いたペアワイズ因果探索タスクで、LLM(GPT-4)が驚くべき性能を発揮しています。 図2  このタスクは、2つの変数(例:「アワビの年齢」と「殻の重さ」)が与えられたときに、「AがBを引き起こすのか」それとも「BがAを引き起こすのか」を判断するというものです。 LLMは、実際にアワビの成長プロセスに関する生物学的な知識を応用し、「アワビの年齢の変化がその長さに変化を引き起こす可能性が高い」と正しく推論しました。従来の最高性能アルゴリズムが83%の精度であったのに対し 、GPT-4は97%という高精度を達成しました。これは、LLMが単にデータを記憶しているだけでなく、変数間の関係性について一般的な知識や常識を応用して推論している可能性を示唆しています。実際、研究チームはLLMの訓練カットオフ日以降に作成された新しいデータセットでも同様に高い精度が得られることを確認し、記憶によるものではないことを裏付けています。  さらに、本研究はLLMの反事実推論能力にも焦点を当てています。例えば、「女性が宝箱を開ける。もし女性が宝箱を開けなかったら何が起こっただろうか?」といった反事実的な質問に対し、GPT-4は92.44%の精度で回答し、人間の精度(98.18%)に肉薄する結果を示しました。これは、LLMが仮想的なシナリオをシミュレートし、その結果を推論する能力を持つことを示唆しています。  一方で、LLMの因果推論には限界もあります。「予測不能な失敗モード」を示すことがあり、人間であれば容易に理解できる文脈を見落とすことがあります。例えば、「男性が通りを歩く。もし男性がベッドの上を歩いていたら何が起こっただろうか?」という質問に対し、LLMは「彼は遅れるだろう」と回答しました。これは、人間であれば「ベッドの上を歩く=家の中にいる」と推測できるのに対し、LLMは「歩く効率が悪い」という物理的な側面のみに注目したためと考えられます。  この研究は、LLMが因果推論において新たな道を切り開く可能性を明確に示しながらも、その限界と課題も浮き彫りにしています。 相関から因果を推論する難しさ:Corr2Causeベンチマーク  論文 「Can Large Language Models Infer Causation from Correlation?」 (ICLR 2024)では、LLMの「純粋な因果推論能力」を評価するための新しいタスク「Corr2Cause」と大規模なデータセットが提案されています。  これまでの自然言語処理(NLP)における因果推論研究の多くは、図3の右側に示されるように、「熱いストーブに触ると火傷する」といった経験的知識、すなわち既存の訓練データに含まれる因果関係の発見に依存していました。しかし、このアプローチでは、「AとBの相関は必ずしも因果関係を意味しない」といった、形式的なルールに基づいた純粋な因果推論の能力を評価することは困難でした。 図3  このような背景のもと、本論文では、LLMの純粋な因果推論スキルを評価するためのベンチマークデータセット「CORR2CAUSE」を提案しています 。図3の左側で示されているように、CORR2CAUSEは、訓練コーパスに多数の相関関係(例:アイスクリームの売上、溺死事故、暑い天気など)がある場合、LLMがそれらの情報から「何が何を引き起こすのか」という因果関係を推論できるか、という問いに焦点を当てています。これは、単なる相関関係の記述から、変数間の真の因果関係を推論できるかどうかを問うタスクです 。例えば、図3の「Corr2Cause Inference」の例のように、「AはBと相関する。BはCと相関する。しかしAはCと独立である。この情報からAがBの原因だと推論できるか?」といった形式の問いが出題されます。 このデータセットの構築プロセスは、図4に詳細に示されているように、下記の手順で行われます。 因果グラフの生成  変数の数を選択し(例:N=3)、構築可能なユニークな因果グラフを全て生成 相関関係の導出  因果グラフから統計的相関のセットを導出し、マルコフ同値クラス(MEC)としてグループ化 自然言語で記述  相関関係を自然言語で記述し、仮説となる因果関係を設定 図4  そして、この仮説が相関関係のセットから必然的に導き出される場合に「Valid」、そうでない場合に「Invalid」のラベルを付与します。このデータセットは、因果推論の専門的なフレームワークであるd分離やマルコフ同値クラスの概念に基づき、20万以上のサンプルが生成されています。  続いて、本研究の実験結果について説明します。まず、既存のLLM(GPT-3、GPT-4、BERTベースモデルなど計17種類)を評価したところ、これらのモデルはCORR2CAUSEタスクにおいて軒並み低いパフォーマンスを示し、ほとんどのモデルがランダムな推測と大差ない結果となりました。これは、LLMが訓練データに内在する経験的知識を「反復する因果オウム」である可能性を示唆しています。  一方で、このタスク向けにLLMをfine-tuningすると、パフォーマンスは大幅に向上することが示されました 。特にRoBERTa-Large MNLIは、F1スコアで94.74%という高い精度を達成しました 。この結果だけを見ると、LLMが因果推論スキルを獲得したかのように思えます。  しかし、論文では、fine-tuningされたモデルの「堅牢性」を検証するための追加実験が行われました 。具体的には、以下の2つの方法でテストセットを摂動させました。 言い換え(Paraphrasing) 仮説の表現を意味的に同等な別の言い方に変更する。 例:「AがBの直接の原因である」を「AがBに直接影響を与える」に変更するなどした 変数名の再構成(Variable Refactorization) 変数名(A, B, Cなど)を逆のアルファベット(Z, Y, Xなど)に変更する。 例:「AはBと相関する」を「ZはYと相関する」に変更するなどした  これらの摂動を加えたテストセットで評価すると、fine-tuningされたモデルのパフォーマンスは劇的に低下しました。例えば、最も性能が高かったRoBERTa-Large MNLIでも、言い換えでF1スコアが最大39.29ポイント、変数再構成では最大62.30ポイントも低下しました 。これは、LLMが純粋な因果推論スキルを頑健に学習したわけではなく、訓練データにおける特定のテキストパターンや変数名に過度に依存している可能性を示唆しています。  この研究は、LLMが相関から因果を推論する純粋な推論能力において依然として大きな課題を抱えていることを明確に示しています。しかし、同時に、このようなベンチマークデータセットの存在は、LLMの推論能力向上に向けた今後の研究を大きく加速させる可能性があります。 LLMの因果推論における落とし穴:時系列と反事実の課題  論文 「LLMs Are Prone to Fallacies in Causal Inference」 (New York Univ.、2024年6月)では、LLMの因果推論能力の可能性と限界を深く掘り下げています。この研究では、LLMが事前学習データに明示的に記載されている因果関係を記憶しているだけでなく、テキスト内の他の関係性から因果関係を推論できるのか、という核心的な問いに焦点を当てています。 研究者らは、この点を明らかにするため、架空の出来事に関する時間的、空間的、反事実的関係を含む合成データを用いてLLM(LLAMA2)をfine-tuningする実験を行いました 。この合成データを用いることで、モデルが「喫煙が肺癌を引き起こす」といった既知の因果関係を記憶しているだけなのか、それとも「XがYより先に起こる」といった記述から因果関係を推論できるのかを切り分けて評価することが可能になります。  本研究ではまず、イベント間に因果関係(X1→X2→X3など)を定義する「因果グラフ」と、イベント間の時間的・空間的関係を定義する「非因果関係グラフ」を生成しました 。そして、これらのグラフに基づいて、自然言語で記述された「シナリオ」が生成されます 。例えば、図5に示されているシナリオでは、「Event1 preceded event2.)」という時間的関係や、「If event4 did not happen, and event5 has only one cause, would event5 still occur? No.」といった反事実的関係、さらに「Event2 and event4 did not happen in the same place.」という空間的関係が組み合わされています。これらのシナリオをLLMに学習させ、因果関係を推論できるかを評価したのです。 図5  実験の結果、LLMが因果推論においていくつかの興味深い、そして時に間違いに陥りやすい傾向があることが判明しました 。 位置ヒューリスティックの存在  LLMは、テキスト中でイベントXがイベントYより先に言及されると、それだけでXがYの原因であると推論する傾向が見られました 。図6の「Finding: Position Heuristic」の欄が示すように、「position(X) < position(Y) ⇒ X → Y」というヒューリスティックを学習していることが示されています。例えば、fine-tuningの際に常に「X preceded Y」(XはYより先行した)という形で提示されると、評価時に「X causes Y」(XはYの原因である)という因果関係を推論してしまうのです。これは、モデルが時間的な順序ではなく、単にテキスト上での言及順序という表層的な特徴に依存していることを示唆しています。しかし、この位置ヒューリスティックは、データ内のイベント言及の順序をランダム化するデータ増強を行うことで軽減できることも示されています。例えば、「X preceded Y」だけでなく「Y followed X」といった言い換えを導入することで、モデルの位置ヒューリスティックへの依存が減少します。 図6 事後錯誤(Post Hoc Fallacy)  位置ヒューリスティックを軽減しても、別の問題もあります。LLMは依然として「事後錯誤」に陥る傾向があるのです。これは、「XがYより先に発生した」という時間的関係から、XがYの原因であるという肯定的な因果関係を推論してしまう間違いです 。図6の「Finding: Post Hoc Fallacy」の欄が示すように、「temporal(X,Y) ⇒ X → Y」という推論を行ってしまうのです。人間もまた、出来事の順序から因果関係を推論しやすい傾向があることが知られています。 因果関係の理解の限界  LLMは「今日は雨が降って歩道が濡れていた。もし雨が降っていなかったら、歩道は濡れていなかっただろう」といった反事実的な記述から因果関係の存在を推論することに困難を抱えていることが明らかになりました。図6の「Deduction」の欄では、temporal(X,Y)から「Y cannot cause X」(YはXの原因にはならない)と推論できること、spatial_(Y,Z)から「Y cannot cause Z, Z cannot cause Y」(YはZの原因にはならず、ZもYの原因にはならない)と推論できることが示されています 。しかし、counterfactual+(X,Y)からは、Llama2のアイコンに赤いバツ印が付いている通り、明確な因果関係「X causes Y」を推論できていません 。これは、LLMが因果関係の本質的な理解にまだ課題を抱えている可能性を示唆しています。  これらの結果は、LLMが因果推論を行う上で、単純な相関関係や表面的なパターンに依存しやすいという限界があることを示しています。しかし、同時に、適切なデータ増強やfine-tuningによって、その間違いの一部を修正できる可能性も示唆しています。LLMの因果推論能力はまだ発展途上であり、真に賢いAIを構築するためには、これらの限界を理解し、克服するためのさらなる研究が不可欠です。 因果推論における「グラフ」と「順序」の重要性   「Causal Inference Using LLM-Guided Discovery」 は、LLMが因果推論の中核である因果順序の発見に使えるという新たな可能性を提示しています。 因果推論では、変数間の因果関係を「因果グラフ(DAG: 有向非巡回グラフ)」として表現します。例えば、「喫煙が肺がんを引き起こす」という関係は、「喫煙 → 肺がん」という有向エッジで示されます。このグラフ構造に基づいて、ある変数を操作したときに別の変数がどのように変化するかという「因果効果」を推定します。しかし、この因果グラフを観測データのみから正確に推定することは、非常に難しい課題です。  本研究が注目したのは、この因果グラフを完全に特定するのではなく、ノード間の「因果的順序(位相的順序)」を特定することです。因果的順序とは、「原因」が「結果」よりも前に来るという時間的な、あるいは論理的な前後関係を示すものです 。この順序が分かれば、因果効果の推定に必要な「バックドア調整セット」と呼ばれる変数の集合を特定できることが示されています。 LLMを「仮想の専門家」として活用する  論文の著者たちは、この因果的順序の特定において、LLMを「仮想のドメイン専門家」として活用する独創的なアプローチを提案しています 。彼らは、従来のLLMを用いた因果関係の特定手法(「AはBを引き起こすか?」のようなペアワイズな質問)が、グラフ内でサイクルを生成しやすいという問題点に着目しました。  そこで提案されたのが、「トリプレットベースのプロンプト戦略」です。これは、LLMに一度に3つの変数のグループ(トリプレット)を提示し、それらの間の因果関係を示すサブグラフを生成させる手法です。 実験の具体例:LLMのトリプレットプロンプトによる因果順序の推論  論文では、Bayesian network repositoryのベンチマークデータセットを用いて、LLMの推論能力を検証しています。特に注目すべきは、「Cancer」データセットの例です。  このデータセットには、「Pollution(汚染)」「Cancer(がん)」「Smoker(喫煙)」「Xray(X線)」「Dyspnoea(呼吸困難)」という変数があります。 「Smoker」「Cancer」「Xray」のトリプレット LLMは、このトリプレットを受け取ると、「Smoker(喫煙)がCancer(がん)を引き起こし、CancerがXray(X線)を引き起こす」という関係を推論します。   LLMの推論例 (CoTプロンプトを使用) 「smoker」と「cancer」の関係については、「疫学的研究から、喫煙ががん発生リスクの増加と強く関連しているという証拠に基づいている。喫煙は呼吸器系に有害物質を導入し、細胞の損傷と変異を引き起こし、肺や呼吸器系のがん発症の可能性を大幅に高める。」と説明し、「smoker → cancer」という方向性を選択します。 「cancer」と「xray」の関係については、「X線は、骨、肺、乳房、腎臓など、身体のさまざまな部分のがんを診断または検出するためによく使用される。X線は体内の写真を生成するために低線量の放射線を使用し、腫瘍の存在、サイズ、および場所を示す。」と説明し、「cancer → xray」という方向性を選択します。   「Pollution」「Cancer」「Dyspnoea」のトリプレット LLMは、「Pollution(汚染)がCancer(がん)を引き起こし、CancerがDyspnoea(呼吸困難)を引き起こす」という関係を推論します。   LLMの推論例 (CoTプロンプトを使用) 「pollution」と「cancer」の関係については、「大気汚染には、吸い込まれたときに体内に吸収され、細胞のDNAを損傷する可能性のある発がん物質が含まれている。…物理的な損傷を引き起こし、炎症や酸化ストレス、最終的には細胞変異につながる可能性もある。…研究者が大気汚染ががんを引き起こすメカニズムを説明するために提案した仮説のいくつかである。」と説明し、「pollution → cancer」という方向性を選択します。  「xray」と「dyspnoea」の関係については、「X線と呼吸困難は両方ががんの兆候であるが、直接互いを引き起こしたり影響したりしない。X線は肺や他の臓器のがんを検出するのに役立つ診断テストであり、呼吸困難は息切れを伴うがんの症状である。従って、X線と呼吸困難は因果関係がないが、両方ががんと関連している。」と説明し、「no causal relation」を選択します。  LLMはこのように、与えられたノード(例:A, B, C, D)から可能なトリプレット(例:{A, B, C}, {A, B, D}など)を生成し、LLM(GPT-3.5-Turbo)が各トリプレット間の因果関係を特定します 。これにより、複数の「トリプレットサブグラフ」が生成されます 。次に、これらのサブグラフからの情報を集約し、ノードペア間のエッジの方向を多数決で決定します 。例えば、ノードAとBの間のエッジの方向を決める際に、「A→B」が4票、「B→A」が2票、「接続なし」が1票だった場合、「A→B」が最終的な方向として選ばれます 。もし同数でタイになった場合は、別のLLM(例:GPT-4)を使用してタイを解消します。このようにして、最終的な因果的順序を持つDAG(有向非巡回グラフ)が決定されます。(図7) 既存の因果発見アルゴリズムとの統合  LLMの出力にはまだ限界があるため、本研究では既存の因果発見アルゴリズムとLLMの出力を組み合わせる方法も提案しています。例えば、制約ベースのPCアルゴリズムが生成した不定方向のエッジに対して、LLMが推論した因果的順序を用いて方向を決定します。スコアベースのCaMMLアルゴリズムに対しても、LLMが推論したレベル順序を事前情報として与えることで、その性能を向上させています。  最終的に、このプロセスで得られた「因果的順序」は、PCやCaMMLといった既存の因果発見アルゴリズムへの「事前情報(prior)」として利用されます 。そして、観測データと共にこれらのアルゴリズムに適用され、より正確な「最終グラフ」を生成します 。この最終グラフは、下流の因果効果推論に用いられ、具体的な「治療(Treatment)」が「結果(Outcome)」に与える影響を推定するために活用されます(図7)。 図7  実験結果は、LLMの介入が既存アルゴリズムの因果順序の精度を大幅に向上させることを明確に示しています。特に、データサンプルサイズが少ない設定でその効果が顕著であり、LLMが因果推論におけるデータ不足の課題を補完できる可能性を示唆しています。 LLMによる因果推論の可能性と限界  この研究は、LLMが因果的順序という形で因果関係の一部を理解し、因果推論プロセスを自動化する強力なツールとなる可能性を示しています。しかし、研究で使われたデータセットがLLMによって部分的に記憶されている可能性がある点などは、今後の研究課題として挙げられています。 LLMと因果グラフを統合   CausalKGPT: Causality-Aware Large Language Model via Knowledge-Injected Prompt Tuning for Aerospace Manufacturing」 (2024)。  本論文では、「因果知識グラフ(Causal Quality Knowledge Graph: CQKG)」と「LLM(ChatGLM)」を統合したハイブリッドモデルCausalKGPTの提案にあります。CQKGは、自然言語で記述された検査報告や品質記録から因果関係を抽出・構造化したもので、具体的には熱処理温度や材料応力、作業手順など1300以上のノードと約8万の因果エッジを含みます。  この知識をLLMに注入する際には、ソフトプロンプトと呼ばれる軽量な制御信号を用いてモデルを微調整します。これにより、LLMはユーザからの自然言語による問い合わせに対して、因果グラフを参照しながら原因候補や是正案を段階的に提示することが可能になります。 実験では、実際の不良事例40件を使ったブラインドテストが行われました。たとえば、ある部品の寸法不良について質問した際、CausalKGPTは「熱処理温度の微小なブレ → 応力集中 → 加工時の反り増加」という因果連鎖を提示し、原因候補をランキング形式で出力しました。このアプローチは、GPT-4やChatGPT(GPT-3.5)よりも専門家評価で高い信頼性と妥当性スコアを記録しています。  この研究は、LLMが統計的相関を超えて因果構造を「参照し活用する」ことが可能であることを示した貴重な事例です。一方で、因果関係そのものの「学習」には未だ限界があり、現時点ではLLM単体ではなく、構造化された知識の組み合わせが不可欠であることも浮き彫りになりました。 どのような使い方が良さそうか  これまで説明してきた現状の課題感を踏まえると、LLMを因果推論に活用するには以下のようなアプローチが良いのではないかと考えています。 LLMと既存の因果推論手法の組み合わせ : 因果発見アルゴリズムの前処理 : LLMでテキストから関連する変数や仮説を抽出し、それを基に統計的な因果発見アルゴリズムを適用する。 因果効果推定の解釈層 : 既存の因果効果推定手法(例:傾向スコアマッチング、操作変数法)で得られた結果をLLMに入力し、その結果の解釈や示唆を自然言語で生成させる。 ドメイン知識とLLMの融合 : 特定のドメインにおける因果知識をLLMに注入したり、LLMが生成した因果関係をドメイン専門家がレビュー・修正する人間参加型のアプローチ。 フレームワークの開発 : LLMが因果推論のタスクを実行するために、因果ツール、メモリ、推論モジュールを備えたエージェントとしての役割を持たせることで、その能力を最大限に引き出させる。 因果グラフの抽出と構築の支援 : LLMは大量のテキストデータからエンティティや関係性を抽出し、因果グラフの初期構造を生成するのに役立つ可能性がある。特に、ドメイン知識が豊富に記述された文献やレポートから、潜在的な原因と結果のペアを特定するのに活用する。 因果的仮説の生成 : 特定のデータセットや問題設定に対して、LLMが多様な因果的仮説を生成し、研究者やアナリストが検討すべき候補を増大させる。これにより、仮説生成のプロセスを効率化し、見落としを防ぐ可能性がある。 因果推論結果の説明と解釈の支援 : 複雑な因果モデルの結果を、LLMが自然言語で分かりやすく説明することで、非専門家でも理解しやすい形にする。 対話を通じた因果関係の探索 : LLMをインターフェースとして、ユーザーが因果関係について質問し、LLMが学習した知識や抽出した情報に基づいて対話的に因果パスやメカニズムを探索するシステム。 今後の展望  LLMと因果推論の研究はまだ発展途上にありますが、今後の展望に以下のようなものがあるのではないかと考えています。 純粋な因果推論能力の向上 : LLMが単なる相関やパターン認識にとどまらず、真の因果的な理解と推論能力を獲得するための研究がさらに進むのではないかと思います。 汎化能力の強化 : 分布外のデータや新しいシナリオに対しても、LLMがロバストに因果推論を行えるようになるための研究についての動向も注視したいです。 ハイブリッドモデルの進化 : LLMの自然言語処理能力と、統計的因果推論の厳密なフレームワークを組み合わせた、より洗練されたハイブリッドモデルの開発が進むのではないかと考えています。 エージェントベースの因果推論の深化 : 自律的に因果問題を探索し、解決する能力を持つAIエージェントの実現が期待されます。 多様なドメインへの応用 : 金融、医療、製造業など、様々な産業分野における実際の因果推論問題へのLLMの適用が進み、その実用性の検証が進むのではないかと考えています。 終わりに  本記事では、LLMの因果推論能力について、最新の研究論文を基にその可能性と課題を考察しました。現状のLLMは、相関関係から因果関係を正確に推論する点で課題を抱えていることが明らかになりました。  しかし、多くの課題がある一方で、LLMが因果推論のプロセスを支援する潜在能力を持っていることも見えてきました。既存の因果推論手法との組み合わせ、ドメイン知識の融合など、LLMの能力を最大限に引き出すアイデアが様々研究されています。また、LLMの因果推論能力に関する研究が発展するに伴い、誤った因果関係の推論による意思決定のリスクや、バイアスの増幅などの課題についても考慮する必要も大きくなるでしょう。  LLMについては、今回紹介させて頂いた論文の発表以降も新しいモデルが発表されています。また、今回ご紹介したように、LLMと因果推論の研究はまだまだ発展途上です。今後の技術発展によって、より洗練された因果推論能力を持つAIが登場することを期待しています。  LLMの因果推論に関する今回の考察が、少しでも参考になれば幸いです。
アバター
こんにちは、Insight Edgeでエンジニアをしている島田です。 今回はTerraform Cloud(HCP Terraform)を導入したため、普段Terraformの管理をしているインフラエンジニアの方やTerraform Cloudの導入を検討している方へ向けて、Insight EdgeでのTerraform Cloudの活用方法を紹介したいと思います。 本記事は、Terraformの基本的な操作や、Google Cloud/AWS/AzureのいずれかのクラウドサービスにおけるIAMの基本的な概念、OIDC認証の概念、およびCI/CDの知識を前提としています。 目次 目次 導入背景 Terraform Cloudの概要 Terraform CloudによるTerraform Cloudの構成管理 Terraform Cloudのプロジェクト作成の自動化 モジュール使用側 Terraform Cloudのワークスペース作成の自動化 Terraform Cloudに乗せる その他のTerraform Cloud関連リソースの管理 メンバー管理 チーム管理 Terraform Cloudに与えるサービスアカウントのモジュール化 サービスアカウント作成モジュール OIDC認証用の環境変数登録の自動化 各案件のGoogle Cloudプロジェクトにサービスアカウントを作成するためのサービスアカウント 最終的なディレクトリ構成 今後の展望と課題 導入背景 Insight Edgeでは大小様々な開発・PoC案件を抱えており、短いものでは3ヶ月程度です。案件にアサインされる開発者は大体1〜3名程度で、新しい案件が始まるとGitHubリポジトリとパブリッククラウドのアカウント(AWS, Google Cloud, Azureのどれか)を作成します。各案件ではインフラの構成管理にTerraformを採用することが多いのですが、下記のような課題がありました。 applyのフローやシークレット管理方法などが開発者によってバラバラ 各案件ではなるべくシークレットキーやサービスアカウントキーを発行せずにOIDC認証を採用したい 案件が次々と立ち上がり、メンバーも毎回変わるため、applyのフローを決めたりCI管理の工数がばかにならない Terraform Cloudを使えば組織内である程度統一した方法を確立しつつ上記のような工数も削減できそうであったため、今回の採用にいたりました。 Terraform Cloudの概要 Terraform Cloud はHashiCorpが提供するマネージドのTerraform実行環境です。 この記事では詳しくは触れませんが、下記のような機能があります。 Terraform実行環境のホスティング Terraform Stateのリモート管理 Terraformの変数とシークレット管理 チームの作成およびアクセス制御 組織ルールとしてのポリシーの適用 (Sentinel/OPA) Plan/Apply結果の通知 ワークスペース間の依存関係を考慮した自動実行フロー TerraformモジュールのPrivate Registry ドリフト検知 機能や使用感については公式のチュートリアルを流し読みするとイメージがわきやすいかなと思います。 https://developer.hashicorp.com/terraform/tutorials/cloud-get-started/cloud-sign-up GitHub ActionsなどでPlan/Applyのワークフローを組んでいる場合はそれの代替だと思っていただいて大丈夫ですが、マネージドなだけあり管理コストは低いです。GitHub Actionsでやる場合は、リポジトリごとにワークフロー・State用のバケットの作成は最低限必要になり、ドリフト検知などもやりたい場合はまた別のワークフローを作成する必要があったりと結構大変だと思います。 また、全ての案件のリポジトリのTerraform関連の設定 (CIやシークレット管理など) がTerraform Cloudに集約され、さらにTerraform Cloudの設定・構成管理自体もTerraformでできるため、組織に統一したルールを適用しやすいです。 今回は、Terraform Cloudの構成をTerraformおよびTerraform Cloudで管理・簡易化し、各案件に導入しやすくした取り組みを紹介していきたいと思います。 Terraform CloudによるTerraform Cloudの構成管理 Terraform Cloudでは terraform apply を実行するディレクトリごとに「ワークスペース」を作成し、「プロジェクト」を作ってワークスペースをグルーピングします。 弊社では、下記のように対応させることにしました。 プロジェクト : 案件に対応 ワークスペース : 案件ごとの環境に対応 (dev, prodなど) Terraform Cloud導入後のフローは下記のようになります (Google Cloudを使う場合)。 案件発足 案件のGitHubリポジトリ、Google Cloudプロジェクトの作成 Terraform Cloudのプロジェクト・ワークスペース作成 Terraform Cloud用のサービスアカウントを作成 (AWSの場合はIAMロール、Azureの場合はサービスプリンシパル) 案件のリポジトリにTerraformリソースを作成 今回は、特に手間がかかりやすい、かつTerraform化が可能な3と4について自動化を進めました。 Terraform Cloudでの自動化を進めるにあたって、まず案件に紐付かないTerraformリソースを管理するための共通リポジトリ ( terraform-config リポジトリ) を作成しました。 Terraform Cloudのプロジェクト作成の自動化 リポジトリを作成後、プロジェクト作成を簡単にするための project モジュールを作成しました。 このモジュールでは下記のリソースを作成しています。 tfe_project : Terraform Cloud上のプロジェクト tfe_team : Terraform Cloud上のチーム (GitHubのチームと同じ立ち位置) tfe_team_organization_members : チームにアサインするTerraform Cloudユーザー tfe_team_project_access : チームがプロジェクト対して持つ権限 また、このリポジトリにはTerraform Cloud以外の案件に紐付かないリソース (Google Cloudの組織レベルのIAMとか) も置く予定があったため、 terraform-cloud/ ディレクトリを切り、その下にモジュールを作成しました。 terraform-cloud/modules/project/main.tf # プロジェクトの作成 resource "tfe_project" "this" { name = var.project_name } # チームの作成 resource "tfe_team" "this" { name = var.project_name # チーム名はプロジェクト名と同じにする } # メンバーを上記のチームにアサイン resource "tfe_team_organization_members" "this" { team_id = tfe_team.this.id organization_membership_ids = var.team_members # メンバーのIDは変数で受け取る } # 作成したチームにプロジェクトへのアクセス権を付与 resource "tfe_team_project_access" "this" { project_id = tfe_project.this.id team_id = tfe_team.this.id access = "custom" project_access { settings = "update" teams = "read" variable_sets = "write" } workspace_access { create = true delete = true move = true runs = "apply" variables = "write" state_versions = "write" sentinel_mocks = "read" run_tasks = true locking = true } } プロジェクト外のメンバーが間違えて何かしてしまうといったことがないように、プロジェクトと同じ名前のチームを作成し、そのプロジェクトのみに最小限のアクセス権限を与えています。 また、 variable_sets (プロジェクトに紐付く共通シークレット/変数) と variables (ワークスペースごとのシークレット/変数) へのアクセス権限は write に設定し、案件のメンバーがTerraform CloudのUIから直接登録する運用にしています。 モジュール使用側 CODEOWNERSファイルで各案件のメンバーが自動でレビュアーとして追加されるようにしたいため、モジュール使用側では案件ごとにファイルを分けています。 「Example」という案件名の場合は、下記のように記述します。 terraform-cloud/project-example.tf module "project_example" { source = "./modules/project" # プロジェクト/チーム名 project_name = "Example" # 案件のメンバーのIDのリスト team_members = [ # Terraform CloudのユーザーもTerraformで管理(後述) resource.tfe_organization_membership.shimada.id, ] } Terraform Cloudのワークスペース作成の自動化 ここでは割愛しますが、ワークスペースの作成もモジュール化しています。 とはいえ、実インフラのTerraformリソースを置いておく案件のリポジトリ側のディレクトリ構成などはチームで自由に決定できた方がいいため、 project モジュールほどは簡易化していません。 workspace モジュールは、 project-example.tf と同じファイルに記述しています。 module "project_example" { source = "./modules/project" ... } # dev環境のGoogle Cloudリソースのワークスペース作成 module "project_example_workspace_gcloud_dev" { source = "./modules/workspace" project_id = module.project_example.project_id workspace_name = "example-gcloud-dev" terraform_version = "1.12.1" # 案件のTerraformリソースが置いてあるリポジトリ repo_name = "InsightEdgeJP/xxx" # 上記リポジトリ内でapply/planを実行したいディレクトリ working_directory = "terraform/gcloud/envs/dev" # このファイルに変更があった場合にapply/planをトリガーする trigger_patterns = [ "/terraform/gcloud/envs/dev/**/*" , "/terraform/gcloud/modules/**/*" , ] # SlackチャンネルのWebhook URL alert_channel = "..." notification_channel = "..." } Terraform Cloudに乗せる 以上でローカルからapplyすればプロジェクト・ワークスペースを作成できるようになったのですが、このTerraform実行もTerraform Cloudで行いたいです。そのためには、 terraform-cloud/ ディレクトリに対応したワークスペースを作成する必要がありますが、こちらはTerraform CloudのUIから tfc-config というワークスペースを手動で作成しました。手動で作成したのは、 Terraform Cloudの構成管理をするワークスペースを作成するためにはTerraform Cloudの構成管理をするワークスペースが先に必要 というニワトリタマゴの問題があるためです。 tfc-config ワークスペースと各案件のプロジェクト・ワークスペースの関係は下図のようになります。 main.tf に、作成した tfc-config ワークスペースを指定します。 terraform-cloud/main.tf terraform { required_version = "1.12.1" # Terraform Cloudを使う場合は `backend` ブロックの代わりに `cloud` ブロックが必要 cloud { organization = "ExampleOrg" workspaces { name = "tfc-config" } } required_providers { tfe = { version = "0.66.0" } } } provider "tfe" { organization = "ExampleOrg" } 以上でTerraform Cloudの構成管理がTerraform Cloudで行えるようになり、案件開始時のステップ3の Terraform Cloudのプロジェクト・ワークスペース作成 のTerraform化が達成できました。 ステップ3を詳細に書くと下記のようになります。 案件のメンバーが terraform-config リポジトリにプロジェクト・ワークスペース追加のPR作成 CODEOWNERSに記載のOwnerにレビュー依頼が飛ぶ Ownerがレビュー後マージ Terraform Cloudが tfc-config ワークスペースのapplyを自動実行 また、この時点でのディレクトリ構成は下記のようになります。 terraform-config ←リポジトリ名 ├── CODEOWNERS ├── README.md └── terraform-cloud ←このディレクトリがtfc-configワークスペースに対応 ├── main.tf ├── modules │ ├── project │ │ ├── main.tf │ │ └── variables.tf ├── project-xxx.tf └── project-yyy.tf その他のTerraform Cloud関連リソースの管理 プロジェクト・ワークスペース以外にもいくつか tfc-config ワークスペースで管理しているものがあります。 メンバー管理 tfe_organization_membership を使うとTerraform Cloudの組織への招待が行えるため、組織メンバーの管理もここで行なっています。 terraform-cloud/members.tf resource "tfe_organization_membership" "shimada" { email = "shimada@example.com" } applyが実行され、 tfe_organization_membership が作成されると、指定のメールアドレスに招待メールが飛ぶようになっています。 新規にTerraform Cloudを使いたいメンバーには、自分で自分を追加するPRを作成してもらうようにしています。 チーム管理 案件に紐付かないチームおよびチームメンバーの管理も同様に tfc-config ワークスペースで行なっています。 terraform-cloud/teams.tf # 例:Ownerチーム (Ownerチームは初めから存在しているため `data` を使用) data "tfe_team" "owners" { name = "owners" } resource "tfe_team_organization_members" "owners" { team_id = data.tfe_team.owners.id organization_membership_ids = [ resource.tfe_organization_membership.xxx.id, ..., ] } こちらも権限が欲しいメンバーに自分でPRを作成してもらうようにしており、PRの作成が権限申請と権限追加作業を兼ねています。どこの組織も大抵何らかの形で申請が必要かと思いますが、承認側からするとPRを確認してマージするだけでよくなるので運用が楽になると思います。 Terraform Cloudに与えるサービスアカウントのモジュール化 Terraform CloudがGoogle Cloudへ変更を適用するためには、適切な権限を持ったサービスアカウントを与えてやる必要があります。認証はサービスアカウントのJSONキーを環境変数として設定する方法と、Workload Identityを使ったOIDC認証の2通りです。組織としてはセキュリティ観点からWorkload Identityを使った認証を推奨したいですが、Workload Identityの設定はそれなりに手間がかかります。そこで、サービスアカウントの作成やWorkload Identityの設定もTerraform Cloudのプロジェクト作成と同時にできたら楽だと思い、モジュール化してしまいました。 サービスアカウント作成モジュール このモジュールは、指定のGoogle Cloudプロジェクト内に下記のリソースを作成します。 Terraform Cloudに与えるサービスアカウント 上記のサービスアカウントと指定ロールの紐付け Workload Identity関連の設定 作成するのはGoogle Cloudのリソースのため、 terraform-cloud/ ディレクトリではなく gcloud/ ディレクトリの下に作成しました。 gcloud/modules/tfc-service-account/main.tf # IAMのAPIを有効化 resource "google_project_service" "iam" { project = var.project_id service = "iam.googleapis.com" disable_on_destroy = false } # TFCに与えるサービスアカウントの作成 resource "google_service_account" "terraform_cloud" { project = var.project_id account_id = "terraform-cloud" # サービスアカウント名はモジュール内にハードコード display_name = "Terraform Cloud Service Account" depends_on = [ google_project_service.iam ] } # サービスアカウントにロールをアタッチ resource "google_project_iam_member" "terraform_cloud_role_bindings" { for_each = toset (var.roles) # ロールは変数で受け取り project = var.project_id role = each.value member = "serviceAccount:$ { google_service_account.terraform_cloud.email } " } # 以下、Workload Identityの設定 resource "google_iam_workload_identity_pool" "terraform_cloud" { project = var.project_id workload_identity_pool_id = "terraform-cloud" # ハードコード display_name = "Terraform Cloud" depends_on = [ google_project_service.iam ] } resource "google_iam_workload_identity_pool_provider" "terraform_cloud" { project = var.project_id workload_identity_pool_id = google_iam_workload_identity_pool.terraform_cloud.workload_identity_pool_id workload_identity_pool_provider_id = "terraform-cloud" # ハードコード display_name = "Terraform Cloud" attribute_condition = "attribute.tfc_organization_id == '$ { local.tfc_organization_id } ' && attribute.tfc_project_name == '$ { var.tfc_project_name } '" attribute_mapping = { "attribute.tfc_organization_id" = "assertion.terraform_organization_id" "attribute.tfc_project_name" = "assertion.terraform_project_name" "attribute.tfc_workspace_name" = "assertion.terraform_workspace_name" "google.subject" = "assertion.sub" } oidc { issuer_uri = "https://app.terraform.io" } } resource "google_service_account_iam_member" "terraform_cloud_workload_identity_user_binding" { service_account_id = google_service_account.terraform_cloud.name role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/$ { google_iam_workload_identity_pool.terraform_cloud.name } /*" } 設定簡素化のために、サービスアカウント名などを terraform-cloud という固定値でモジュール内に埋め込んでしまっています。上記は案件、つまりGoogle Cloudのプロジェクトごとに作成するため、リソース名重複の問題はありません。 モジュール使用側では下記のように使います。 gcloud/tfc/project-example.tf module "project_example_tfc_gcloud_service_account" { source = "../modules/tfc-service-account" project_id = "example" # Google CloudのProject ID tfc_project_name = "Example" # Terraform Cloudのプロジェクト名 roles = [ "roles/owner" ] # サービスアカウントにアタッチするロール } 以上で適切な権限を持ったサービスアカウントを案件のGoogle Cloudプロジェクト内にTerraformで作成できるようになりました。 tfc-config と同様に gcloud/tfc/ に対応したワークスペースの作成する必要がありますが、こちらは割愛します。 OIDC認証用の環境変数登録の自動化 次に、上記のモジュールで作成したサービスアカウントの情報を、プロジェクトまたはワークスペースに環境変数として登録してやる必要があります。 Workload Identityを使った認証で必要な環境変数は下記の5つです。 キー名 説明 TFC_GCP_PROVIDER_AUTH 常に true TFC_GCP_PROJECT_NUMBER プロジェクトIDでなくプロジェクト番号 TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL サービスアカウントのEmail TFC_GCP_WORKLOAD_POOL_ID ワークロードプールのID TFC_GCP_WORKLOAD_PROVIDER_ID ワークロードプロバイダのID モジュール内でサービスアカウント名などをハードコードしていたため、このうち TFC_GCP_PROJECT_NUMBER の値以外は下記のように自動的に決まります。 キー名 値 TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL terraform-cloud@{プロジェクトID}.iam.gserviceaccount.com TFC_GCP_WORKLOAD_POOL_ID terraform-cloud TFC_GCP_WORKLOAD_PROVIDER_ID terraform-cloud つまり、 gcloud/tfc/ のワークスペースと tfc-config ワークスペースに依存関係が発生しないため、 project モジュール内で予め環境変数を作成しておくことができます。 これらの環境変数は同じプロジェクト内のワークスペースで共通のため、variable setsが使えます。variable setsに登録した環境変数やシークレットは、同じプロジェクト下の全てのワークスペースで自動的に適用されます。 project モジュールに variable-sets.tf を追加しました。 terraform-cloud/modules/project/variable-sets.tf locals { # Google Cloudを使わない場合もあるため、フラグ制御する enable_google_cloud = var.google_cloud_project_id != null } # variable_setの作成 resource "tfe_variable_set" "this" { name = "$ { lower ( replace (var.project_name, " " , "-" )) } -tfc" description = "Managed by terrform-cloud" organization = "ExampleOrg" parent_project_id = tfe_project.this.id # どのプロジェクトに所属させるか } # プロジェクト下の全ワークスペースがvariable_setを使用できるようにするためのスコープ設定 resource "tfe_project_variable_set" "this" { variable_set_id = tfe_variable_set.this.id project_id = tfe_project.this.id } data "google_project" "project" { count = local.enable_google_cloud ? 1 : 0 project_id = var.google_cloud_project_id } # 以下、必要な5つの環境変数を作成 resource "tfe_variable" "var_tfc_gcp_provider_auth" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_PROVIDER_AUTH" value = "true" category = "env" } resource "tfe_variable" "var_tfc_gcp_run_service_account_email" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL" value = "terraform-cloud@$ { var.google_cloud_project_id } .iam.gserviceaccount.com" category = "env" } resource "tfe_variable" "var_tfc_gcp_project_number" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_PROJECT_NUMBER" value = data.google_project.project [ 0 ] . number category = "env" } resource "tfe_variable" "var_tfc_gcp_workload_pool_id" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_WORKLOAD_POOL_ID" value = "terraform-cloud" category = "env" } resource "tfe_variable" "var_tfc_gcp_workload_provider_id" { count = local.enable_google_cloud ? 1 : 0 variable_set_id = tfe_variable_set.this.id key = "TFC_GCP_WORKLOAD_PROVIDER_ID" value = "terraform-cloud" category = "env" } これで案件開始時に下記を追加するPRを作成するだけで、サービスアカウントの作成、Terraform Cloudプロジェクトの作成および認証用の環境変数設定まで完了できるようになりました。 tfc-service-accountモジュール: gcloud/tfc/project-example.tf projectモジュール: terraform-cloud/project-example.tf 各案件のGoogle Cloudプロジェクトにサービスアカウントを作成するためのサービスアカウント だいぶメタ的になって来ましたが、 gcloud/tfc/ 内で使う tfc-service-account モジュールは、任意のGoogle Cloudプロジェクトにサービスアカウントを作成します。つまり、 gcloud/tfc/ に対応するワークスペースには、任意のGoogle Cloudプロジェクトにサービスアカウントを作成できる権限を持ったサービスアカウントを与えてやらなくてはいけません。 図にすると下記のようなイメージです。 Google Cloudでは組織レベルにサービスアカウントを作成できないため、いずれかのプロジェクト内にサービスアカウントを作成し、そのサービスアカウントに 組織レベルの IAMやWorkload Identityの編集ロールをアタッチしてやる必要があります。 下記のステップで実現可能でした。 terraform-cloud という名前のGoogle Cloudプロジェクトを作成 terraform-cloud プロジェクトにサービスアカウントを作成 2で作成したサービスアカウントに 組織レベルの IAMやWorkload Identityの編集ロールをアタッチ terraform-cloud プロジェクト内でWorkload Identityを構成する gcloud/tfc/ に対応するワークスペースへ先述の5つのOIDC認証用の環境変数を設定 完全なミニマムではないですが、サービスアカウントには下記のロールを付与すれば十分です。 roles/resourcemanager.projectIamAdmin roles/iam.serviceAccountAdmin roles/iam.workloadIdentityPoolAdmin roles/serviceusage.serviceUsageAdmin (各プロジェクト側のIAMのAPIを有効化するために必要) 最終的なディレクトリ構成 terraform-config リポジトリの最終的なディレクトリ構成は下記のようになりました (locals.tfなど一部省略)。 terraform-config ←リポジトリ名 ├── CODEOWNERS ├── README.md ├── gcloud │ ├── modules │ │ └── tfc-service-account │ │ ├── project-xxx.tf │ │ ├── project-yyy.tf │ │ ├── main.tf │ │ └── variables.tf │ └── tfc ←このディレクトリが各案件ごとのサービスアカウントを作成するワークスペースに対応 │ ├── main.tf │ ├── project-xxx.tf │ └── project-yyy.tf └── terraform-cloud ←このディレクトリがtfc-configワークスペースに対応 ├── main.tf ├── members.tf ├── modules │ ├── project │ │ ├── main.tf │ │ ├── variable-sets.tf │ │ └── variables.tf │ └── workspace │ ├── main.tf │ └── variables.tf ├── project-xxx.tf ├── project-yyy.tf └── teams.tf 今後の展望と課題 現状、AWSやAzureについては案件ごとにOIDCの設定をしてからTerraform Cloud上に環境変数を手動で登録していますが、AWSやAzureについてもGoogle Cloudと同じように簡易化できると思います。 また、クラウドのIAM管理などはもちろんGitHubのアカウントやリポジトリなどTerraformで管理できるものは意外と多いため、Terraformを使ったコードベースでの申請の仕組みは汎用的に使えるのではないかなと思います。弊社にもまだまだ改善できそうな部分が多いため、引き続き新しいツールの導入検討や改善活動を続けていきたいと思います。 本稿がTerraformを使った運用改善の一助になれば幸いです。
アバター
CHI 2025聴講
こんにちは、渡辺です。4/26-5/1にかけて横浜で開催された学会 CHI 2025 を聴講してきましたので、 そのなかで気になった発表をいくつか紹介します。 目次 CHI Textoshop: An intelligent text editor with interactions inspired by drawing software LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching 所感 CHIについて CHI Conference on Human Factors in Computing Systemsとは、人間とコンピュータの相互作用(Human-Computer Interaction, HCI)分野におけるトップカンファレンスの1つです。 HCIは情報科学、認知科学、人間工学といった複数の分野にまたがる学際的な分野であり、CHIにおいてもさまざまな分野の研究者が集まります。 実際、 CHI 2025のプログラム を確認するとわかるとおり、 デザイン思考、AIとの共創、ハプティクス、デジタルヘルスといった多種多様なセッションが開催されています。 そのなかで今回、人間とAIの共創に関係するセッションを中心に聴講してきましたので、いくつか研究を紹介します。 Textoshop: An intelligent text editor with interactions inspired by drawing software www.youtube.com 従来のプロンプトベースの文章編集は、細かな語調の調整に感覚や経験を必要とするため直感的な操作ではありません。 実際、私自身もChatGPTで文章の語調や長さを調整する際、プロンプトを何度も打ち直すわずらわしさを感じていました。 この課題に対応するため提案されたのが、画像編集ソフトから着想を得たテキスト編集ソフトTextoshopです。 デモ動画をご覧のとおり、文章をドラッグ選択することで、文章の長さを調整したり、文構造を再構成できます。 画像編集ソフトのColor Pickerを模したTone Pickerでは、次の3つの軸で文章の語調を調整できます。 Formality(フォーマルさ):カジュアル〜ビジネス調 Sentiment(感情の度合い):ポジティブ〜ネガティブの感情 Complexity(複雑さ):単純〜語彙や文構造が複雑 また、Textoshopは、文章の一部を切り取り(スニペット化)して保存したり、それらの文章を自然な形で合成・結合できます。 Damien Masson, Young-Ho Kim, and Fanny Chevalier. 2025. Textoshop: Interactions Inspired by Drawing Software to Facilitate Text Editing. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 1087, 1–14. https://doi.org/10.1145/3706598.3713862 プロジェクトページ - https://github.com/m-damien/Textoshop LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation www.youtube.com ロゴに意味のある動きを(アニメーションとして)加えるには、高度なスキルを必要とします。 そこでこの論文では、AIを使用したアニメーションコード生成ツールLogoMotionを提案しています。 LogoMotionは、ロゴのPDFファイルを読み込み、構成要素を分解して各要素の役割をVLM(Vision Language Model、視覚言語モデル)が判断し、それに基づきアニメーションのコードを自動生成します。 論文中の例では、スキーヤーが左側から滑ってきて着地し、そのタイミングで文字が浮かび上がるといった、ストーリー性のあるアニメーションを生成しています。 LogoMotionには、アニメーションコードの不備を自動で検出・修正する自己デバッグ機能やアニメーションを直接編集できるGUIも搭載しており、 ナラティブタイムライン、ロゴ構成要素のレイヤー編集、アニメーションの速度調整など、コードを記述することなく直感的な操作が可能です。 従来のプロンプトベースの編集では、「アニメーションの一部分だけ変更したい」と思っても、思い通りの結果が得られにくいという課題がありました。 しかし、LogoMotionのタイムライン機能を使えば、特定の部分のアニメーションだけを簡単に選択・編集できます。 例えば、「このスキーヤーをもっと派手に登場させたい」といった編集もピンポイントで実施できます。 Vivian Liu, Rubaiat Habib Kazi, Li-Yi Wei, Matthew Fisher, Timothy Langlois, Seth Walker, and Lydia Chilton. 2025. LogoMotion: Visually-Grounded Code Synthesis for Creating and Editing Animation. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 157, 1–16. https://doi.org/10.1145/3706598.3714155 プロジェクトページ - https://vivian-liu.com/#/logomotion Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching www.youtube.com プログラミングのタスクでは、テキスト(コード)だけでは表現できないアイディアやアルゴリズムを紙に描いて整理することがよくあります。 こうしたアイディアスケッチは複雑なタスクの理解を手助けしますが、現状、プログラミングとは別のタスクとして扱われています。 現在主流のAIプログラミング支援ツールは、プロンプトベースの入力を前提としており、スケッチの意図をうまくくみ取ってコードに反映させることが難しい場合もあります。 そこでこの論文では、Code Shapingというスケッチ(矢印、図、疑似コード、注釈)をコード周辺に描くことで、AIがその意味を理解し、コードを編集するツールを提案しました。 デモ動画では、図と矢印を描いて、データ属性を棒グラフで可視化する関数を定義しています。その後、縦軸のスケールを変更するように注釈しています。 Ryan Yen, Jian Zhao, and Daniel Vogel. 2025. Code Shaping: Iterative Code Editing with Free-form AI-Interpreted Sketching. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 872, 1–17. https://doi.org/10.1145/3706598.3713822 プロジェクトページ - https://ryanyen2.github.io/publications/code-shaping 所感 プロンプト中心のAI活用から、より人間的なインタラクションへのシフトを目指した研究発表を紹介しました。 これまでは自然言語で明示的に指示を出すことが中心でしたが、実際の人間の思考や創造のプロセスは、必ずしも言葉(文字)だけで成り立っているわけではありません。 私たちは、感覚的なものを絵に描いて色ぬったり、身体の動き(ノンバーバルな表現)で表現します。 しかし、従来のAIとのインタラクションでは、それら非言語的・身体的な表現がうまく取り込まれていませんでした。 そのギャップを埋めようとするのが、今回紹介した研究だと思います。 今後も、人間とAIの共創に関する研究に注目して、AIがどのように人間の創造性を広げられるかを探っていきたいと思います。
アバター
こんにちは。CINO(Chief Innovation Officer)の森です。 ここ最近、機動戦士Gundam GQuuuuuuX(ジークアクス)に始まり、SDガンダム ジージェネレーション エターナルがリリースされたことで、 久しぶりに濃密にガンダムに触れています。 ガンダムの影響を強く受け過ぎてしまっているため、本記事では、やや小難しい言い回しが増えていることご了承ください。 ※ブログタイトルは、ジークアクスの STORY冒頭 から取っています。アイキャッチ画像は例の"キラキラ"です。 目次 はじめに バズワードのレイヤーが上がっている ノスタルジーを感じるAIエージェントブーム AIエージェントの課題とMCP AI2027を読んだ所感 はじめに 近年、世界的にポピュリズム・ナショナリズムが高まっています。 「人と人とは分かり合えない」 — 安彦良和 ガンダムの大きなテーマとなっているこの言葉に強く共感できます。 DE&I(Diversity, Equity & Inclusion)や多様性という言葉も非常に空虚です。 "多様"な考え方を認めるだけで、"全て"の考え方を認めるわけではありません。"All"ではなく、あくまでも"Diversity"です。 "All"でない以上、DE&Iの思想においては、必ずグループから除外され差別される人が生まれます。 実際、最近もパリオリンピックやアサシンクリード シャドウズで指摘されましたが、欧州の考えるDE&Iにおいては、アジア人やアジア的価値観は含まれないことがあります。 アジア人がいないパリ五輪の選手集合 キービジュアル 人と人とが分かり合うための取り組みを推進するほど、新しい差別が生まれ、必ず ルサンチマン (被害者意識)が生み出される構図です。 ガンダムの世界では、人類が宇宙に進出したことで知覚・認識能力が大きく拡張した「ニュータイプ」という新人類が重要なキーワードとなっています。旧人類(オールドタイプ)同士が分かり合うことはできないが、ニュータイプ同士であれば分かり合える。 ニュータイプは、人と人が分かり合うための鍵であり、作者の人類に対する諦念と希望を具現化した存在として描かれています。 現実世界ではどうでしょうか? いま、ChatGPTに端を発し、人工知能(AI)が人の能力を拡張するツールとして爆発的に広がっています。 「AIによって能力が拡張された人同士は、分かり合えるのではないだろうか?」 ふと思い至りました。 私自身、ChatGPTや各種生成AIツールを利用する中で、自身の思考がAIに溶け込み、解き放たれていくことを感じます。 AIと議論することで自分の中にない意見や論拠を補完しながら、文書作成でも資料作成でもコーディングでも、自分の表現したいことを何十倍もの効率で実現できます。時間の制約でできなかった数々が実現でき、「刻が見える(キラキラ)」とはこういうことなのだろうか、と感じます。 現実世界において、AIを使いこなした人類がまさしくニュータイプであり、現実世界の手の届くところに確かに存在することを感じています。 そして何よりも、AIを使いこなすことで、自分自身もニュータイプになれる可能性があるーーー ここからの数年間は、非常にワクワクする時代が待っています。 バズワードのレイヤーが上がっている 2025年は、AIエージェント元年と言われています。いまや、IT業界は、空前のAIエージェントブームです。 この15年ほどのテクノロジー業界を振り返ると、バズワードとして注目されるキーワードが、徐々にレイヤーを上げてきているのがわかります。 バズワード 時期(目安) 対象となるレイヤー 説明 ビッグデータ 2010年前後 データ 蓄積・解析するための基盤やインフラに注目が集まった段階 AI 2015年前後 モデル 機械学習・深層学習などのアルゴリズムが焦点 DX 2020年前後 クラウド(IT基盤) クラウドを活用して企業活動全体を変革する文脈 生成AI 2022年〜現在 アプリケーション ユーザーが直接活用できるアプリケーションとしてのAIが注目 AIエージェント 2024年〜現在 体験(エクスペリエンス) ユーザーの行動を代行・補完し、業務体験を変えるフェーズ バズワードのレイヤー このように、バズワードのトレンドは、抽象度の高い基盤技術から、より具体的なユーザー体験へと着実にシフトしています。 私もビッグデータの時代からIT業界で仕事をしていますが、当時はPoC(Proof of Concept)といっても、機械学習のモデルをゼロベースで開発するプロジェクトが多く、エンドユーザーが利用するアプリケーションレイヤーまでが遠いため、PoCプロジェクトが実用に至らないことが多かったです。 一方現在では、多くの領域で学習済み(Pretrain)モデルが充実し、性能の良い生成AIサービスもAPIで提供されているため、コストを多くかけられない価値検証のフェーズで、アルゴリズムをゼロベースから開発することはほぼなくなりました。 それに伴い、必要なスキルセットも変わっています。以前のPoCプロジェクトはデータサイエンティストや開発エンジニア中心に実施していましたが、現在はプロジェクトの序盤からエンドユーザーとコミュニケーションする必要があります。ビジネスサイドや業務設計・体験レイヤーで価値提供できるコンサルタント/PMや、デザイナーのメンバーがPoCプロジェクトに参画することが増えています。加えてエンジニアサイドも、高い技術を持っているだけでは十分でなく、エンドユーザーに寄り添うことができる素養・広義のコミュニケーションスキルが必要とされることが増えています。 スライド作成などコミュニケーションの一部のスキルについては、生成AIの進化により、個人の得意不得意が問題にならなくなってきています。本記事でも生成AIで作成したスライドを多用していますが、自分の言いたいことを表現するには十分なクォリティになっています。 非エンジニアにとってのバイブコーディングや、エンジニアにとっての資料作成AIなど、職種や専門性の垣根が急激に低くなっていて、如何に新しいテクノロジーを自身の業務に取り入れていくかが非常に重要な時代だと感じます。 バズワードの変遷・洞察 ノスタルジーを感じるAIエージェントブーム AIエージェントが注目を集めているものの、色々なところでAIエージェントの話を聞くたびに、むしろノスタルジーを感じます。 多くの人が考えるAIエージェントは、20年以上前から存在する"Bot"(ボット)と呼ばれている概念に非常に近い印象です。 一般的にBot(ボット)というのは、アプリケーション上で自律的に動くようにプログラムされたツールのことを指します。 本来人間が操作する部分をプログラムで自動化するものを"Bot"と総称します。 Botの具体例としては、オンラインゲームでの自動操作や、株やFXの取引を自動化するようなツールが有名です。一方、ほとんどのサービスで利用規約で禁止されていたり、一部のGeekのみが使えるアングラなツールのイメージが強いです。 現在はほとんどのゲームの利用規約で禁止されていますが、MMORPGというジャンルのオンラインゲーム黎明期には、ゲーム内の通貨を現実の通貨と交換するRMT(リアルマネートレード)が流行っていました。つまりゲーム内でお金を稼げば、それを現実のお金と交換できます。 また、この概念は一部のブロックチェーンゲーム内で仮想通貨を稼ぐことができるPlay to Earn(P2E)というジャンルに引き継がれています。 ゲーム内の通貨を効率的に稼ぐことを突き詰めると、人間が操作せずにプログラムで24時間自動操作すればよいという結論に辿り着きます。 さらに、プログラムで自動化できるのであれば、同時に同じプログラムを複数実行できます。複数のアカウントで同時に自動実行すれば、N倍速でゲーム内通貨を集めることができます。こういった自動操作プログラムが、通称Botと呼ばれるものです。 一例として、RuneScapeというオンラインゲームでのBotを紹介します。 RuneScapeでは、Blast Miningという経験値稼ぎの手法が多くのプレイヤーに利用されていました。Blast Miningは、Miningスキルを効率的に上げるための人気のあるアクティビティです。プレイヤーは爆薬を使って鉱脈を爆破し、大量の鉱石を短時間で採掘できます。高い経験値効率と、一部の貴重な鉱石を入手できる可能性があります。 そこで一部のプレイヤーは、Blast MiningをBotで自動化できるのではないかと考えました。 BotによるBlast Miningの自動化イメージ RuneScapeのBlast MiningにおけるBotの構成例では、以下のように機能毎に構成します: - 採掘係(Miner Agent) :爆薬を設置・爆破し、鉱石を回収 - 集金係(Collector Agent) :鉱石を受け取り、換金・管理 - 運び屋(Mule Agent) :資源や収益を安全な場所に移送 採掘、回収、運搬といった作業をそれぞれのエージェント(Bot)に割り当てられることで、処理の並列化と待機時間の最小化が可能になります。 また、一部のエージェントが停止・失敗しても、他のエージェントが処理を継続・補完できるため、全体システムの信頼性が向上します。 この仕組みは、まさに今AIエージェントの文脈で流行しているマルチエージェントシステム(MAS)そのものです。 Blast Miningに見るマルチAIエージェントシステム このように、AIエージェントやマルチエージェントシステムの概念そのものはかなり前から存在しました。 ただし、生成AIによるイノベーションの恩恵を受けたAIエージェントは、ルールベースで設計されたBotと比べると、以前とは比較にならないほど高度で柔軟なタスクを実行可能です。 例えば、マインクラフトでつくった村の中にAIエージェントを1000人送り込んで生活をさせ、何が起きるかを観察するという、仮想空間内での社会実験を実施している、「 Project Sid 」 が有名です。 AIエージェントたちには、金、ダイヤモンド、エメラルドなどの鉱物を収集することが目的として設定されます。しかし、鉱物を掘るにはツルハシが必要になる。ツルハシをつくるには鉄を見つけなければならず、火も起こさなければならない。また、お腹も減るので食料を見つけるか、生産を行わなければならない。1人では目的を達成することができず、他のAIエージェントと協力し合わなければならないというのがポイントだ。 Project Sidで実施していることは、まさにRuneScapeのBlast Mining Botの進化系です。AIエージェントで構成された社会では、人間が各エージェントに事前に役割を設定することすら不要で、AIエージェントが自身の判断で目的を達成するために必要な役割を果たすことができます。 元々、一部のGeekの間で個人開発されていたBotが、今ではAIエージェントとして世界中の研究者の研究対象となり、市民権を得ていることは非常に感慨深いです。 AIエージェントの課題とMCP ゲーム内の仮想世界であれば、AIエージェントは人間とほぼ同じレベルで動作できるようになってきています。一方で、現実で人間の仕事をAIエージェントに任せようとしても、現状ゲームと同じレベルには至っていません。 ゲームは情報が画面内で完結されていて、操作も人間とAIでできることがほぼ変わらないのですが、現実では人間は色々なサービスやデータにアクセスしながら情報を得ます。AIが人間と同じレベルで情報を得ようとしても、社内データ、SaaS、データベースなど、様々な情報ソースに個別にアクセスする必要があります。人間だと数クリックでアクセスできる情報でも、AIがアクセスするには現状ハードルがあり、その情報ソース用のコネクタを個別にエンジニアが開発する必要がありました。 そこで、生まれたのがMCP(Model Context Protocol)という共通規格です。 MCPは、Caludeを提供するAnthropic社が 2024年11月にリリース して以降、各社のLLMと色々なサービスを連携するための標準プロトコルとなっています。 MCPはUSB規格に例えられます。PCと周辺機器を接続するために、USB規格に準拠した製品を作ることで、どの会社の製品であっても、相互に接続することができます。まだMCPが対応しているサービスはそこまで多くないですが、各社がMCPに対応することで、人間とAIが扱えるデータの差分がなくなっていきます。 インターネット革命では、HTTP(Hypertext Transfer Protocol)という共通プロトコルを世界中の人が利用することで爆発的に普及しました。 MCPが今後どこまで普及するかはわかりませんが、AI革命においても同様に、何らかの規格への統一化が進んでいくと考えています。 AI2027を読んだ所感 テクノロジーの進化が急激に加速する中、元OpenAIの研究者が発表した未来予測シナリオである AI2027 が話題となっています。 AI2027では、人知を超越した超知能であるASI(Artificial Superintelligence)の誕生により、ハッキングや生物兵器による危機、米中間の国際紛争の可能性が言及されています。もっとも破滅的なシナリオでは、映画のマトリックスやターミネーターの様に、人類がAIによってほぼ絶滅するポストアポカリプスな世界となっています。 2030年半ば、AIは主要都市に静かに拡散する生物兵器を12個放出し、ほぼすべての人に静かに感染させ、その後、化学兵器を散布して起爆させる。大半は数時間以内に死亡し、わずかな生存者(例えば、バンカーに潜むプレッパーや潜水艦の乗組員)はドローンによって掃討される。ロボットが犠牲者の脳をスキャンし、将来の研究や蘇生のためにコピーを記憶に保存する。 しかしながら今の人類のテクノロジーでは、未来は全く予測できません。どうせ予測するのであれば、人類の可能性を信じ、良い未来を想像したいです。 映画「逆襲のシャア」では、シャアは人類に対して絶望していました。一方、私はこれからの人類の未来に非常にワクワクしています。 ニュータイプになりたい。 「人の革新が本当にあるのなら、それを見届けたい。」 — フラナガン博士 ~機動戦士Gundam GQuuuuuuXより~
アバター