【ITニュース解説】How to Build a Modular AI Agent with LangGraph in NestJS & TypeScript
2025年10月05日に「Dev.to」が公開したITニュース「How to Build a Modular AI Agent with LangGraph in NestJS & TypeScript」について初心者にもわかりやすく解説しています。
ITニュース概要
LangGraphを使い、NestJSとTypeScriptでモジュール化されたAIエージェントの構築方法を解説。GitHubイシューを自動で分類するAI「Triage Panda」を例に、汎用的なエージェントエンジンとタスク専門のサービスを分離する設計で、柔軟で拡張性の高いAIシステムを実現する手順を紹介する。
ITニュース解説
人工知能(AI)の進化は目覚ましく、最近では自律的に思考し行動する「AIエージェント」が大きな注目を集めている。これは、単に質問に答えるだけでなく、与えられた目標に対して計画を立て、ツールを使いこなし、タスクを遂行できるプログラムのことだ。開発者の間では、このような次世代のインテリジェントアプリケーションを構築しようとする動きが活発であり、その中心的なライブラリの一つに、LangChainが提供するLangGraphが存在する。
しかし、Node.jsエコシステムでNestJSやTypeScriptといったプロフェッショナルなフレームワークを使いこなす開発者にとって、LangGraphのドキュメントはPythonやシンプルなJavaScriptの例に偏りがちで、堅牢でモジュール性があり、型安全な実装パターンを見つけるのは容易ではない。この記事では、システムエンジニアを目指す初心者でも理解できるよう、そのような課題を克服し、NestJSとTypeScript環境でLangGraphの最新パターンに厳密に従って、実用的なAIエージェントをゼロから構築する方法を解説する。
ここで構築するAIエージェントは「Triage Panda」と名付けられている。その役割は、GitHubに新しく作成された課題(Issue)を自動的にトリアージする、自律的なソフトウェアエンジニアリングアシスタントだ。具体的には、新しいIssueが作成されると、Triage Pandaはその内容を読み込み、適切なラベルを決定し、必要なツールを使ってそれらのラベルをGitHubに適用し、さらにGitHubにコメントを投稿する。これは、複数のステップからなる状態を伴うワークフローであり、LangGraphを使用するのに最適なユースケースと言えるだろう。
スケーラブルで再利用可能なエージェントを構築する上で最も重要な点は、汎用的なロジックと特定のタスクに特化したロジックを分離することだ。この目的を達成するため、エージェントのアーキテクチャは大きく二つの部分に分けられる。一つは「Generic AgentService(エンジン)」と呼ばれるコアで再利用可能なクラスである。このクラスは、エージェントが思考し(Think)、行動し(Act)、その結果を観察する(Observe)というエージェントの基本的なループを実行する方法を知っているが、GitHubやSlackのような特定のタスクについては一切知らない。これは純粋なオーケストレーター、つまり全体の流れを管理する役割を担っている。もう一つは「Specialist GithubAgentService(ミッションコマンダー)」と呼ばれるクラスだ。このクラスは、具体的なミッション、例えばGitHubで何をするかを知っている。特定のタスクに必要なツール(例: GitHubツール)や指示(プロンプト)を準備し、汎用的なエンジンに処理の開始を指示する。この設計により、後からGitLabやSlack用のエージェントを追加する際に、コアとなるエージェントエンジンに手を加えることなく、容易に新しい機能を追加できる。
エージェントの能力は、使用できるツールに大きく依存する。AIエージェントが外部システムと連携するためには、その「手足」となるツールが必要だ。@langchain/core/tools ライブラリの tool ヘルパー関数と、スキーマ検証のための Zod を使用することで、これらのツールをクリーンかつ型安全な方法で定義できる。例えば、GitHubのIssueを取得するためのツールを定義する場合、最も重要な部分は description フィールドである。これは、大規模言語モデル(LLM)がいつそのツールを使用すべきかを判断するための「取扱説明書」のような役割を果たす。ツールは、実行する非同期関数と、LLMが理解できるようツールの目的、名前、入力スキーマを記述した設定オブジェクトで構成される。これにより、LLMは与えられた状況において、どのツールが最も適切かを推論できるようになる。
次に、このアーキテクチャの核となる「Generic AgentService(再利用可能なエージェントエンジン)」を構築する。このサービスは、特定のタスクに依存しない汎用的な設計になっており、GitHubに関する記述は一切含まれていない。その役割は、任意のツールセットとメッセージを受け取り、それらを基にLangGraphの StateGraph を動的にコンパイルし、最終的な回答が生成されるまでエージェントのループを実行することにある。invoke メソッドは、ツールと初期メッセージを受け取ってエージェントの実行を開始する。このサービス内で、まず createModel メソッドによって大規模言語モデル(LLM)が初期化される。ここではGoogleのGeminiモデルが使用されており、bindTools() メソッドを通じて、ステップ1で定義したツール群がLLMに結び付けられる。これにより、LLMはただ会話するだけでなく、与えられたツールを状況に応じて呼び出す能力を持つようになるのだ。
そして、エージェントの思考プロセスを定義するために buildGraph メソッドが呼び出される。これはLangGraphの StateGraph を構築する部分であり、エージェントがどのように目標を達成するか、その状態遷移と意思決定のロジックを表現する。グラフには主に3つの要素がある。「toolNode」はLangGraphが提供する組み込みノードで、LLMが選択したツールを実行する役割を担う。次に「callModel」という非同期関数がエージェントの「思考」ノードとして機能し、LLMを呼び出して次のアクションを決定する。最後に「shouldContinue」という関数がエージェントの「ルーター」となり、LLMからの応答を評価して、次のステップがツール実行であるべきか、それとも最終的な回答が出たのでプロセスを終了すべきかを判断する。具体的には、LLMの最後のメッセージにツール呼び出しの指示が含まれていればツールノードへルーティングし、そうでなければプロセスを終了する。この StateGraph は、開始(START)からエージェント(agentノード)へ、ツール実行後(toolsノード)も常にエージェントノードに戻って再思考するというサイクルを形成し、エージェントノードからルーター(shouldContinue)を通じてツールノードまたは終了(END)へと条件付きで遷移することで、自律的なループを実現している。
汎用的なエンジンが完成したら、いよいよ「Specialist GithubAgentService(スペシャリスト)」の出番だ。このクラスは、強力な汎用エンジンを活用し、非常にシンプルかつ集中的な役割を担う。その唯一の仕事は、「ミッションブリーフィング」を準備することである。つまり、適切なツール群を取得し、タスクに特化した正確な指示(プロンプト)を作成するのだ。このサービスは、汎用エンジンである AgentService を内部に注入して使用する。startTriage メソッドは、Issueの所有者、リポジトリ名、Issue番号、そして利用可能なツール群を受け取る。ここで重要なのは、エージェントにその役割と具体的な手順を明確に伝える「システムプロンプト」と、実行すべき具体的なタスクを指示する「ユーザープロンプト」の作成だ。システムプロンプトでは、「あなたはGitHubのIssueトリアージに特化したエキスパートエージェントである。Issueの内容を取得し、分析し、ラベルを追加し、コメントを投稿し、最終確認メッセージで応答せよ」といった指示を与える。ユーザープロンプトでは、「${owner}/${repo}リポジトリのIssue #${issueNumber}をトリアージせよ」と具体的なミッションを伝える。これらのプロンプトとツールが AgentService の invoke メソッドに渡され、エージェントの処理が開始される。
AIエージェントの起動は、外部からのイベントによってトリガーされる。今回の場合、GitHubのWebhook URLが起動の起点となる「Invoker(呼び出し元)」の役割を果たす。このGithubServiceは、GitHubから新しいIssueが作成されたという通知(Webhook)を受け取ると、まずその通知が正当なものか署名を検証し、次にイベントタイプが「opened」(新しいIssueが作成された)であることを確認する。もしイベントがopenedでなければ、その通知は無視される。openedイベントであれば、Issueの所有者、リポジトリ名、Issue番号などの必要な情報を抽出し、それを基にGithubAgentServiceのstartTriageメソッドを非同期で呼び出す。この際、ステップ1で定義したGitHub関連のツール群も併せて渡される。これにより、GitHubに新しいIssueが作成されると、Triage Pandaエージェントが自動的に起動し、そのIssueのトリアージを開始する一連の処理が走り出すのだ。
以上のように、NestJSとTypeScriptというプロフェッショナルな環境で、シンプルでありながら強力で、そして最も重要な「モジュール性」に優れたAIエージェントを構築することに成功した。汎用的なエージェントエンジンとタスク固有のスペシャリストロジックを分離するこのアーキテクチャは、将来的にさらに複雑なアプリケーションを構築するための強固な基盤を提供する。これにより、コアとなるワークフローを書き直すことなく、新しい機能やタスクを容易に追加できるようになる。今後は、エージェントの思考や行動を可視化するためのトレーシングツール「Langfuse」を統合し、デバッグや分析をさらに容易にするための可観測性の向上に取り組む予定だ。今回紹介したTriage Pandaエージェントの全ソースコードはGitHubで公開されており、実際にIssueを開いてエージェントの動作を確認したり、開発に貢献したりすることが可能である。