イントロダクション:AIエージェント開発、最初の「壁」
「デモ版のチャットボットなら1日で動いたのに、自社ツールと連携させようとした途端、泥沼にハマってしまった」
こうした課題は、多くのAI開発現場で共通して報告されています。生成AI、特にLLM(大規模言語モデル)の進化は目覚ましいですが、それを実業務のワークフローに組み込み、自律的に動く「エージェント」として機能させるには、単なるプロンプトエンジニアリングとは別次元の設計力が求められます。
特に、OpenAI APIの「Function Calling(関数呼び出し)」機能が登場して以来、LLMは「話すだけの存在」から「行動できる存在」へと進化しました。しかし、その実装において、多くのエンジニアが「とりあえずLangChainを使えばいいのか?」「それともLangGraphのような新しいフレームワークを導入すべきか?」という技術選定の岐路で立ち尽くしています。さらに、2026年現在ではライブラリの重大な脆弱性対応(CVE-2025-68664等)やSDKの仕様変更といったエコシステムの変化も激しく、判断を難しくさせています。
本日は、AI導入プロジェクトの現場を知り尽くしたプロジェクトマネージャーである鈴木恵さんに、AIエージェント開発の「現実解」を伺います。華やかなデモ動画の裏側にある、泥臭くも重要な設計の勘所。これを押さえるかどうかが、PoC(概念実証)止まりで終わるか、本番稼働できるかの分かれ道です。
なぜFunction Callingの実装で躓くのか
インタビュアー(以下、I): 鈴木さん、本日はよろしくお願いします。早速ですが、多くのエンジニアがFunction Callingの実装、特にエージェント化の段階で躓いてしまうのはなぜでしょうか?
鈴木恵(以下、鈴木): よろしくお願いします。一番の理由は、「確率的な挙動をするAI」と「決定的な挙動を求めるシステム」の接続部分におけるミスマッチですね。
従来のシステム開発では、入力Aに対して必ず出力Bが返ってきます。しかしLLMは、同じプロンプトでも揺らぎがある。Function Callingを使えばJSON形式で構造化データが返ってくるとはいえ、その中身の整合性までは保証されません。存在しない商品IDを引数に指定してきたり、必須パラメータを勝手に省略したりする。
多くのエンジニアは、APIドキュメント通りに実装すれば動くと考えがちですが、エージェント開発においては「AIは間違えるものである」という前提に立った防御的な設計が不可欠なんです。
また、技術トレンドの変化も見逃せません。以前はLangChainのAgent機能一択のような状況でしたが、現在はより複雑な状態管理(ステート管理)が可能なLangGraphが推奨されるようになっています。「とりあえずライブラリがよしなにやってくれるだろう」と楽観視してスタートすると、後で保守性の問題に直面し、痛い目を見ることになります。
本日のゲスト:AI実装の現場を知り尽くしたアーキテクト
I: まさに「現場の声」ですね。鈴木さんはSIerでの堅実なプロジェクトマネジメント経験と、最新のAI技術への知見を併せ持ち、AI駆動PMの専門家として活動されています。技術的な理想論だけでなく、ビジネスとしてのROI(投資対効果)や保守性まで見据えたアドバイスには定評があります。
鈴木: ありがとうございます。AIはあくまで手段に過ぎません。どんなに高度なエージェントでも、現場で使い続けられなければ意味がありません。今日は、流行りのツールに飛びつく前に知っておくべき、足腰の強い設計論についてお話しできればと思います。
Q1 概念理解:Function Callingは「魔法の杖」ではない
I: まずは基礎的な認識合わせから入りたいと思います。Function Callingについて、「AIが勝手に関数を実行してくれる機能」という理解で止まっている方も多いようですが、実際はどうなのでしょうか?
鈴木: その認識は半分正解で、半分間違いです。そして、その「間違い」の部分が設計ミスを招く元凶になります。
正確には、Function Callingは「AIが関数を実行する」機能ではありません。「AIが『実行すべき関数名』と『その引数』を決定し、JSON形式で出力する」機能です。
I: なるほど。「実行」そのものはAIが行うわけではないと。
鈴木: その通りです。ここが非常に重要です。
- ユーザーがプロンプトを入力する。
- LLMが「このタスクにはあの関数が必要だ」と判断し、JSONを生成して返す。
- (ここ重要)我々のシステム側でそのJSONを受け取り、パースし、実際の関数(API叩くなりDB検索するなり)を実行する。
- その実行結果を再びLLMに渡して、最終的な回答を生成させる。
この「3」のプロセスは、完全に従来のプログラミングの世界です。つまり、Function Callingを使ったからといって、自動的に何かが動くわけではなく、開発者が「実行ランタイム」を実装しなければならないのです。
LLMが「道具」を使う仕組みの本質
鈴木: LLMを「脳」、外部ツール(APIやDB)を「手足」と例えるなら、Function Callingは脳から手足への「神経伝達信号」に過ぎません。実際に筋肉を動かすのは、開発者が記述するコードです。
この分離を理解していないと、「なぜAIが勝手にDBを書き換えないんだ?」とか、逆に「いつの間にか危険な操作をされてしまった」といった混乱が生じます。
プロンプトエンジニアリングとの決定的な違い
I: 以前はプロンプトで「JSON形式で出力してください」と指示して無理やり構造化データを作らせていましたよね。それとは何が違うのですか?
鈴木: 信頼性と精度が段違いです。プロンプトでの指示はあくまで「お願い」レベルですが、Function Calling(特に最近のOpenAI APIのTool use)は、モデル自体がその構造を出力するようにファインチューニングされています。
ですが、それでも「100%」ではありません。型定義(スキーマ)に従わないJSONが返ってくることも稀にあります。だからこそ、後ほどお話しする「バリデーション(検証)」の設計が重要になってくるんです。
Q2 技術選定の岐路:LangChain vs ネイティブ実装
I: ここが今日一番聞きたかったポイントです。AIエージェントを開発する際、LangChainのようなフレームワークを使うべきか、それともOpenAI SDKなどを使ってネイティブに実装すべきか。この論争に終止符を打ってください。
鈴木: 非常に悩み深い問題ですね。結論から言えば、「フェーズとチームの習熟度による」のですが、これでは答えになっていませんよね。もう少し踏み込んで、専門家としての見解をお伝えします。
「検証(PoC)やハッカソンならエコシステムが豊富なLangChain。長期運用する本番プロダクトなら、ネイティブ実装(または必要最小限の自作ラッパー)を推奨する」
これが、現時点での結論です。特に最近では、LangChainのエコシステム内でもより制御性を重視したLangGraphが登場し、選択肢が広がっていますが、それでも「依存を最小限にする」という原則は重要です。
フレームワーク利用のメリットと「隠れたコスト」
I: LangChainは非常に便利で、数行でエージェントが動きますよね。なぜ本番運用では慎重になるべきなのでしょうか?
鈴木: おっしゃる通り、立ち上がりの速さは圧倒的です。かつてのinitialize_agentのような高レベルAPIは魅力的でした。しかし、その裏側で何が起きているかがブラックボックス化されすぎている点がリスクになります。
例えば、エージェントが意図しないループに陥ったときや、プロンプトの一部だけを微調整したいとき、フレームワークの抽象化レイヤーが厚すぎて、どこを直せばいいのか分からない、という事態は珍しくありません。また、独自の記法(LCELなど)や、LangGraphにおける「グラフ構造」や「状態管理」の概念を覚える学習コストも、意外と高くつきます。
さらに、LLMのAPIは進化が非常に早いです。プロバイダーが新しい機能(例えば最新のキャッシュ機能や構造化出力など)を出したとき、フレームワーク側の対応版リリースを待たなければならないという「依存性のリスク」も無視できません。
ネイティブ実装を選ぶべき具体的なシチュエーション
I: 逆に、ネイティブ実装(SDKを直接利用)のメリットは?
鈴木: 「透明性」と「制御性」です。自分でループ処理やメッセージ履歴の管理を書くのは確かに初期工数がかかります。しかし、「今どんなプロンプトが送られ、どんなレスポンスが返ってきたか」が完全に掌握できます。
特にエラーハンドリングにおいて、きめ細やかな対応が可能です。「JSONパースエラーならこうリトライする」「APIタイムアウトならこうする」といった分岐を、ビジネスロジックに合わせて自由に書けるのは大きな強みです。
自社プロダクトとして、コア機能にLLMを組み込むなら、こちらを強く推奨します。ブラックボックスがないため、問題発生時の原因切り分けが非常にスムーズだからです。
【比較表】開発フェーズ別・推奨アーキテクチャ
ここで、判断の助けになるよう比較軸を整理しましょう。
| 評価軸 | LangChain / LangGraph | ネイティブ実装 (公式SDK等) | 推奨シナリオ |
|---|---|---|---|
| 開発スピード | ◎ 非常に速い。豊富なツール群やドキュメントローダーが即利用可能 | △ 初期は遅い。メッセージ管理や実行ループを自作する必要あり | PoC、ハッカソン、多種多様なツール連携が必要な場合 |
| 学習コスト | △ 高い。独自概念(Chain, Graph, State等)の理解が必要 | ◯ 標準的。PythonとAPI仕様が分かれば読める | チームメンバーの入れ替わりが激しい場合 |
| デバッグ性 | ◯ LangGraph Studio等で可視化は進んだが、内部挙動の追跡は複雑な場合あり | ◎ printデバッグやログ出力が容易で、処理の流れが完全にホワイトボックス | 複雑なビジネスロジックを含む場合 |
| カスタマイズ性 | ◯ 拡張可能だが、フレームワークの作法に従う必要あり | ◎ 完全に自由。独自のメモリ管理や最適化が可能 | 応答速度やコストを極限までチューニングしたい場合 |
| 保守性 | △ ライブラリのアップデート頻度が高く、破壊的変更への追従コストがかかる | ◎ APIの変更追従は必要だが、影響範囲を特定しやすく依存が少ない | 長期間(数年単位)運用する基幹システム |
鈴木: もちろん、LangChainも進化しており、従来の「Chain」ベースから、より制御しやすい「LangGraph」へとトレンドが移行しています。これにより、以前よりはループ処理や状態管理が明示的に書けるようになりました。
しかし、「まずは仕組みを理解する」という意味でも、一度はネイティブで書いてみることを強くお勧めします。LLMとの対話の生々しい挙動(Raw Response)を知っているかどうかで、トラブルシューティングの勘が全く違ってくるからです。
Q3 実装の勘所:不安定なAIをどう飼い慣らすか
I: 方針が決まったところで、具体的な実装の話に入りましょう。Function Callingを実装する際、特に気をつけるべきポイントはどこですか?
鈴木: 「スキーマ定義」と「エラー復帰」。この2点に尽きます。
スキーマ定義が精度の8割を決める
鈴木: Function Callingの精度は、関数の説明(description)とパラメータの定義で8割決まると言っても過言ではありません。
多くのエンジニアは、関数名だけ適当につけて、説明文(description)を空欄にしがちです。しかし、LLMはこの説明文を読んで「いつ、どうやってこの関数を使うべきか」を判断しています。
例えば、search_product という関数があるとして、説明文が「商品を検索する」だけでは不十分です。
「ユーザーが具体的な商品名や型番を指定した場合に使用する。曖昧なカテゴリ検索には使用しないこと」
ここまで具体的に書いて初めて、意図通りに動きます。
また、Pythonなら Pydantic を使って厳密に型定義を行うのがベストプラクティスです。LangChainもPydantic V2への移行を完了しており、最新のPython環境でも高い互換性と型安全性を確保できるようになっています。Enum(列挙型)を使って、引数の値を「"A", "B", "C"」のいずれかに強制的に限定するような設計は、ハルシネーション(幻覚)を防ぐための基本テクニックです。
エラーハンドリングとリトライ設計の定石
鈴木: 次にエラーハンドリングです。AIは平気で嘘をつきますし、定義していない引数を渡そうとすることもあります。
ここで重要なのが、「エラーをAIにフィードバックする」というループです。
バリデーションエラーが発生した場合、単に例外を投げて終了するのではなく、「引数 date の形式が不正です。YYYY-MM-DD 形式で再入力してください」というエラーメッセージをAIのコンテキストに追加して、再度推論させます。これで多くのケースは自動修復可能です。
また、ライブラリ自体の「不安定さ」への対応も実装の重要な勘所です。AIのエコシステムは変化が激しく、依存ライブラリの更新が頻繁に発生します。
注意すべき最新の動向:
- セキュリティ更新への追従:例えば、LangChain Coreではシリアライゼーションに関する重大な脆弱性(CVE-2025-68664)が報告されています。APIキー流出のリスクがあるため、常に最新のセキュリティパッチが適用されたバージョンを使用するようバージョン管理を徹底する必要があります。
- 廃止予定機能への対応:Google Vertex AI SDKの生成AIモジュールのように、将来的に廃止が決定し、新しいSDK(Google Gen AI SDK)への移行が必須となるケースもあります。
こうした外部環境の変化に強い設計にするためには、AIとの対話部分を適切に抽象化し、ライブラリの変更がビジネスロジック全体に波及しないよう設計しておくことが「堅牢性」の鍵となります。
【ケーススタディ】API連携失敗時のリカバリーフロー
I: それでも解決しない致命的なエラーのときはどうすればいいですか?
鈴木: そこで Human-in-the-loop(人間参加型) の設計が登場します。
AIがどうしても判断できない、あるいはツール実行に失敗し続ける場合は、人間にエスカレーションするフローを組み込みます。
LangChainのエコシステムを例に挙げると、最新のAgent Serverでは「RemoteCheckpointer」のような機能が強化されています。これはエージェントの実行状態(ステート)を保存し、エラー時に人間が介入して修正した後、そこから処理を再開できるようにする仕組みです。また、TTL(Time To Live)管理機能により、古い状態データを適切にクリーンアップしながら最新の状態を維持することも可能です。
自前で実装する場合も、この考え方は非常に参考になります。処理の途中でステートをデータベースに永続化し、「承認待ち」や「エラー確認待ち」といったステータスを設けることで、AI任せにしない確実な業務フローを構築できます。
コメント