10 人が閲覧(直近 30 日) AIエージェント・MCP

LangChain.jsとは?npm導入からcreateAgent・構造化出力・会話メモリまで【v1.5対応】

LangChain.jsとは?npm導入からcreateAgent・構造化出力・会話メモリまで【v1.5対応】

LangChain.jsは、LLMを使うアプリやAIエージェントをJavaScript/TypeScriptで作るためのフレームワークで、PythonのLangChainと同じ考え方(モデル・プロンプト・ツール・エージェント)をNode.jsやブラウザ、エッジ環境で扱えます。日本語の解説には2023〜2024年のv0系のコード(LLMChainやAgentExecutor)が今も多く残っていますが、v1系ではエージェントの作り方がcreateAgentに一本化されました。本記事では2026年10月28日時点の最新版(langchain 1.5.20、@langchain/core 1.3.0)を実際にインストールし、APIキーなしで動かした結果をもとに、導入から実装、旧コードの移行先までを説明します。

まとめ:LangChain.jsの要点

  • 導入:npm install langchain @langchain/coreに、使うプロバイダのパッケージ(例:@langchain/openai)とzodを足す。Node.jsは22以上を使う。
  • エージェント:createAgent({ model, tools })で作る。OpenAIのGPT-6.1 Solにツールを使わせるなら、useResponsesApi: trueを付けたモデルを渡す(文字列指定のままだとChat Completionsへ送られる)。
  • 会話履歴:checkpointerを渡し、呼び出しごとにthread_idを指定する。指定しないとエラーで止まる。
  • 構造化出力:responseFormatにzodのスキーマを渡すと、検証済みの結果がstructuredResponseに入る。toolStrategyではスキーマ違反が既定で自動再試行される。
  • 旧API:LLMChainやRetrievalQAChainは@langchain/classicへ移った。新規コードでは使わない。

LangChain.jsの位置づけと対応環境

LangChain.jsはLangChain社がGitHubのlangchain-ai/langchainjsで開発しているMITライセンスのOSSで、npmではlangchainという名前で配布されています。v1系のエージェントはLangGraph(状態を持つ実行基盤)の上に作られており、npm install langchainを実行すると@langchain/langgraphも依存として入ります。

公式READMEは対応環境として、Node.js(ESMとCommonJS)、Cloudflare Workers、Vercel/Next.js(ブラウザ・Serverless・Edge)、Supabase Edge Functions、ブラウザ、Deno、Bunを挙げています。CommonJSからのrequire("langchain")も1.5.20で動作しました(パッケージはdist/index.cjsとdist/index.jsの両方を持ちます)。

必要なNode.jsの版

Node.jsの要件は、見る場所によって数字が違います。結論としてNode.js 22以上を使ってください。langchain本体のenginesは20以上ですが、公式のインストール手順は「Requires Node.js 22+」と書いており、OpenAI統合の@langchain/openai 1.7.1はengines自体が22以上です。Node.js 20は2026年4月30日にサポートが終了しています。

確認した場所 Node.jsの要件
langchain 1.5.20のengines 20以上
@langchain/core 1.3.0のengines 20以上
@langchain/openai 1.7.1のengines 22以上
公式インストール手順・v1移行ガイド 22以上
Node.js 20のサポート終了 2026-04-30

Node.jsの版の選び方はNode.jsの仕組みと版の選び方で、ESMとCommonJSの混在で詰まる場合はESMとCommonJSの違いと移行手順で詳しく扱っています。

Python版との使い分け

基本の抽象(チャットモデル、メッセージ、ツール、Runnable)はPython版と同じ名前で揃っていますが、関数名の書き方が違います(Pythonのcreate_agentはJSではcreateAgent、system_promptはsystemPrompt)。どちらを選ぶかは、機能の差よりも、アプリ本体をどの言語で書いているかで決めるのが実務的です。

JavaScript/TypeScript版を選ぶ場面

Next.jsやExpressなどNode.jsのバックエンドにLLM機能を組み込む場合は、JS版を選びます。別言語のAPIサーバーを1つ挟まずに済み、ツールの引数をzodで定義すれば入力の型がそのままTypeScriptの型になります。エッジやブラウザで動かしたい場合も、選べるのはJS版だけです。

Python版を選ぶ場面

PDFの解析や埋め込みの前処理、評価用のデータ処理など、Pythonのライブラリに頼る工程が中心ならPython版を選びます。社内の検証コードがPythonで書かれているのに本番だけJS版へ書き直すと、同じ処理を2言語で保守することになります。前処理はPython、画面に近い部分はJS、と分けるならAPI境界をはっきり決めてから分けてください。

LangChain.jsのインストールとパッケージ構成

v1系は、必要なパッケージだけを足していく構成です。2026年10月28日時点のnpmの最新版は次のとおりです(公開日はnpmレジストリの記録で、協定世界時)。

パッケージ 最新版(公開日) 役割
langchain 1.5.20(2026-10-27) createAgent・tool・ミドルウェア
@langchain/core 1.3.0(2026-10-22) メッセージ・Runnable・プロンプト
@langchain/openai 1.7.1(2026-10-27) OpenAIのモデルと埋め込み
@langchain/anthropic 1.6.0(2026-10-22) Claudeのモデル
@langchain/langgraph 1.5.3(2026-10-26) 実行基盤・MemorySaver
@langchain/classic 1.0.56(2026-10-27) v0系の旧チェーン
npm install langchain @langchain/core @langchain/openai zod
export OPENAI_API_KEY="sk-..."

本記事のコードは、package.jsonに"type": "module"を書いたプロジェクトでagent.mjsのように1例ずつファイルに保存し、node agent.mjsで実行する前提です(トップレベルのawaitを使うため)。この4つを空のプロジェクトに入れると、追加されたパッケージは25個、node_modulesは105MBでした。大きいのはopenai(33MB)と、トークン数を数えるjs-tiktoken(21MB)です。AWS Lambdaのようにデプロイ容量に上限がある環境では、この大きさを前提にバンドル方法を決めてください。

@langchain/coreのバージョン統一

@langchain/coreのREADMEは、使っているLangChainのパッケージがすべて同じ版の@langchain/coreを共有する必要があると書いています。各パッケージはcoreをpeerDependencyとして持つため通常は1つにまとまりますが、古い統合パッケージが混ざると2つの版が入り、基底クラスまわりの型エラーが出ます。npmではpackage.jsonに次のoverridesを足すと、直接依存に書いた版へ固定できます(1.5.20の構成で、全6か所が1.3.0に揃うことをnpm ls @langchain/coreで確認しました)。

{
  "dependencies": {
    "@langchain/core": "^1.3.0",
    "langchain": "^1.5.20"
  },
  "overrides": {
    "@langchain/core": "$@langchain/core"
  }
}

initChatModelによるチャットモデルの呼び出し

一番短い呼び出しはinitChatModelです。"openai:gpt-6.1-sol"のように「プロバイダ:モデル名」の文字列を渡すと、対応するプロバイダのパッケージが読み込まれます。モデルIDは提供元の更新が速いので、使う前に各社の公式のモデル一覧で確かめてください。

import { initChatModel } from "langchain";

// "プロバイダ:モデル名" の文字列で、対応するプロバイダパッケージが読み込まれる
const model = await initChatModel("openai:gpt-6.1-sol");
const res = await model.invoke("LangChain.jsを1文で説明して");
console.log(res.content);

プロバイダのパッケージを入れていない文字列(例:"anthropic:...")を渡すと、Unable to import @langchain/anthropic. Please install with `npm install @langchain/anthropic`というエラーになります。文字列指定は便利ですが、依存の追加漏れは実行するまで見つかりません。

LCELによるプロンプトとモデルの接続

エージェントが要らない定型処理(要約・分類・翻訳)は、@langchain/coreのプロンプトとモデルをpipeでつなぐLCELで書きます。

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { initChatModel } from "langchain";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "あなたは{role}です。"],
  ["human", "{question}"],
]);
const model = await initChatModel("openai:gpt-6.1-sol");
const chain = prompt.pipe(model).pipe(new StringOutputParser());

console.log(await chain.invoke({ role: "社内ヘルプデスク", question: "VPNが切れる" }));

テンプレートの変数を渡し忘れると、モデルを呼ぶ前にMissing value for input variable `question`で止まります。

createAgentによるツール付きエージェントの実装

v1系のエージェントはcreateAgentで作ります。ツールはtool関数に処理本体と、名前・説明・zodの引数スキーマを渡して定義します。次の例は、在庫を答えるツールを持ち、会話の履歴も保持するエージェントです(ツールは固定値を返す教材用で、本番では在庫DBやAPIの呼び出しに置き換えます)。

import { createAgent, tool } from "langchain";
import { ChatOpenAI } from "@langchain/openai";
import { MemorySaver } from "@langchain/langgraph";
import * as z from "zod";

// 教材用の固定値。本番では在庫DBやAPIを呼ぶ処理に置き換える
const getStock = tool(
  async ({ sku }) => `${sku}の在庫は12個です`,
  {
    name: "get_stock",
    description: "商品コードから在庫数を返す",
    schema: z.object({ sku: z.string().describe("商品コード") }),
  },
);

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-6.1-sol", useResponsesApi: true }),
  tools: [getStock],
  systemPrompt: "在庫の質問にはget_stockで確認してから答える。",
  checkpointer: new MemorySaver(),
});

const config = { configurable: { thread_id: "user-1" } };
const r1 = await agent.invoke(
  { messages: [{ role: "user", content: "A-100の在庫は?" }] },
  config,
);
console.log(r1.messages.at(-1).content);

// 同じthread_idなら前の会話を踏まえて答える
const r2 = await agent.invoke(
  { messages: [{ role: "user", content: "さっきの商品をあと何個発注すべき?" }] },
  config,
);
console.log(r2.messages.at(-1).content);

モデルをnew ChatOpenAI(...)で渡しているのには理由があります。OpenAIのGPT-6.1 Solのモデルページは「Tool calling requires the Responses API」と書いています。ローカルに擬似サーバーを立てて送信先を記録したところ、model: "openai:gpt-6.1-sol"の文字列指定と、オプションなしのChatOpenAIは/v1/chat/completionsへ送り、useResponsesApi: trueを付けると/v1/responsesへ送りました(@langchain/openai 1.7.1)。ツールを渡すエージェントでは、モデルごとにこの条件を確かめてください。

モデルを後述の擬似モデルに差し替えて実行すると、1回目のmessagesはhuman → ai → tool → aiの4件でした。モデルがツール呼び出しを返し、LangChainがツールを実行して結果をtoolメッセージとして戻し、モデルが最終回答を書く、という往復がそのまま記録されます。

checkpointerとthread_idによる会話履歴の保持

履歴はcheckpointerがthread_idごとに保存します。ツールとsystemPromptを持たない最小のエージェントに、毎回ユーザーのメッセージを1件ずつ渡して、1.5.20で確かめた挙動は次のとおりです。

呼び出し方 モデルに渡ったメッセージ数
thread_id=x の1回目 1
thread_id=x の2回目 3(前の質問と回答を含む)
thread_id=y の1回目 1(xの履歴は見えない)
checkpointerなしで2回 1(履歴は残らない)
checkpointerありでthread_idなし エラーで停止

最後のケースはFailed to put checkpoint. The passed RunnableConfig is missing a required "thread_id" fieldというエラーです。ユーザーやセッションのIDをthread_idに入れる処理を、API層で必ず通してください。MemorySaverはプロセスのメモリに置くだけなので、再起動すると履歴は消え、サーバーを複数台・複数プロセスで動かすと、別のプロセスに届いたリクエストからは同じ履歴を参照できません。本番ではデータベースに保存するcheckpointerを選びます。

zodスキーマによる構造化出力の取得

問い合わせの分類や項目抽出のように、結果をプログラムで扱いたい場合はresponseFormatにスキーマを渡します。公式ドキュメントによると、zod・Standard Schema互換のスキーマ・JSON Schemaを渡せて、既定ではツール呼び出しの形で出力させ、モデルがネイティブの構造化出力に対応していればそちらを使います。

import { createAgent, toolStrategy } from "langchain";
import { ChatOpenAI } from "@langchain/openai";
import * as z from "zod";

const Ticket = z
  .object({
    category: z.enum(["請求", "障害", "その他"]),
    urgent: z.boolean(),
  })
  .meta({ title: "Ticket" });

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-6.1-sol", useResponsesApi: true }),
  tools: [],
  responseFormat: toolStrategy(Ticket),
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "本番環境が落ちてログインできません" }],
});
console.log(result.structuredResponse); // { category: '障害', urgent: true } の形

toolStrategyはtoolsが空でも構造化出力用のツールをモデルに渡すため、ここでもResponses APIを使う設定にしています。実行して分かった点が2つあります。1つ目は、スキーマのtitleがそのまま構造化出力用のツール名になることです。付けないとextract-1のような連番の名前になり、ログやトレースで読みにくくなります。2つ目は、モデルがスキーマに合わない値(列挙にない"雑談"や文字列の"yes")を返したときの扱いです。既定ではエラー内容をモデルに返して再生成させ、2回目の正しい値がstructuredResponseに入りました。toolStrategy(Ticket, { handleError: false })にすると再試行せず、StructuredOutputParsingErrorを投げます。再試行はモデル呼び出しの回数と料金を増やすので、件数の多いバッチ処理では、後述のmodelCallLimitMiddlewareで1回の実行あたりのモデル呼び出し回数に上限を決めておいてください。

ストリーミングの配信単位とevent streaming

チャット画面で回答を少しずつ表示する方法は2つあります。従来からのstreamは、streamModeで流れてくる単位が変わります。

streamMode 流れてくるもの(ツール1回の実行例)
updates model_request → tools → model_request
values 各段階の状態全体(メッセージ1→2→3→4件)
messages LLMのトークン単位の断片とメタデータ(ノード名など)

ノード名には注意が必要です。v1移行ガイドは、ストリーミングのノード名が"agent"から"model"に変わったと書いていますが、1.5.20で実際に流れてきた名前はmodel_requestでした。v1系としてnpmに最初に公開された安定版1.0.1(2025-10-18公開)の配布物でもaddNode("model_request")です。ガイドどおり"model"で絞り込むと、何も表示されません。

新しく作るアプリについて、公式のストリーミング解説はLangChain v1.3で入ったevent streaming(streamEventsのversion: "v3")を勧めています。メッセージ・ツール呼び出し・最終状態を別々の反復で受け取れるので、streamModeの組を分岐で読み分ける必要がありません。擬似モデルで実行すると、ツール呼び出しget_stock {"sku":"A-100"}と最終回答のテキストがそれぞれの反復に流れ、stream.outputで4件のメッセージを持つ最終状態が取れました。

import { createAgent } from "langchain";

const agent = createAgent({ model: "openai:gpt-6.1-sol", tools: [] });

const stream = await agent.streamEvents(
  { messages: [{ role: "user", content: "LCELを短く説明して" }] },
  { version: "v3" },
);
for await (const message of stream.messages) {
  for await (const delta of message.text) process.stdout.write(delta);
}
const finalState = await stream.output;

ミドルウェアによるモデル・ツール呼び出し回数の制限

v1系では、モデル呼び出しやツール実行の前後に処理を差し込むミドルウェアをmiddleware配列で渡せます。最初に入れておきたいのは回数の上限です。同じツールを呼び続ける擬似モデルで試した結果は次のとおりでした。

  • toolCallLimitMiddleware({ runLimit: 2 }):2回まではツールを実行し、3回目のツール結果がTool call limit exceeded. Do not make additional tool calls.に置き換わって終了した。
  • modelCallLimitMiddleware({ runLimit: 3, exitBehavior: "end" }):モデルを3回呼んだところでModel call limits exceeded: run level call limit reached with 3 model callsを最後のメッセージにして終了した。
import { createAgent, toolCallLimitMiddleware, modelCallLimitMiddleware } from "langchain";
import { ChatOpenAI } from "@langchain/openai";

const agent = createAgent({
  model: new ChatOpenAI({ model: "gpt-6.1-sol", useResponsesApi: true }),
  tools: [],
  middleware: [
    toolCallLimitMiddleware({ runLimit: 5 }),
    modelCallLimitMiddleware({ runLimit: 10, exitBehavior: "end" }),
  ],
});

ほかに、長い会話を要約するsummarizationMiddleware、ツール実行前に人の承認を挟むhumanInTheLoopMiddleware、個人情報を伏せるpiiMiddlewareなどもlangchainから直接importできます。外部APIを叩くツールを持たせる場合、上限なしで本番に出すのは避けてください。

fakeModelによるAPIキー不要のエージェントテスト

ここまでの実行結果は、langchainに含まれる擬似モデルfakeModelで取ったものです。返す応答を順番に積んでおけるので、APIキーも料金もかけずに、ツールの呼び出し順や履歴、構造化出力の検証をユニットテストに書けます。

import { createAgent, tool, fakeModel, AIMessage } from "langchain";
import * as z from "zod";

const getStock = tool(async ({ sku }) => `${sku}の在庫は12個です`, {
  name: "get_stock",
  description: "商品コードから在庫数を返す",
  schema: z.object({ sku: z.string() }),
});

// 1回目はツール呼び出し、2回目は最終回答を返す擬似モデル
const model = fakeModel()
  .respondWithTools([{ name: "get_stock", args: { sku: "A-100" } }])
  .respond(new AIMessage("A-100の在庫は12個です。"));

const agent = createAgent({ model, tools: [getStock] });
const r = await agent.invoke({ messages: [{ role: "user", content: "A-100の在庫は?" }] });

console.log(r.messages.map((m) => m.type).join(" -> ")); // human -> ai -> tool -> ai
console.log(r.messages[2].content);                       // A-100の在庫は12個です
console.log(model.calls.length);                          // 2

積んだ応答を使い切ってからさらに呼ばれると、FakeModel: no response queued for invocation 1のようなエラーになります。想定より多くモデルが呼ばれたことがテストで分かるので、この挙動はそのまま回数の検証に使えます。なお、本記事の実行結果は擬似モデルとローカルの擬似サーバーで取ったもので、実際のOpenAI APIで生成させた回答の内容は検証の範囲に含みません。Python版のテスト用モデルとの違いはFakeLLMの使い方と落とし穴にまとめています。

v0系のLangChain.jsコードのv1系への移行

検索で見つかる日本語のサンプルには、v0系のコードが多く残っています。v1移行ガイドに沿って、主な書き換え先を整理します。

v0系の書き方 v1系での扱い
createReactAgent(@langchain/langgraph/prebuilts) langchainのcreateAgent
prompt オプション systemPrompt
preModelHook/postModelHook ミドルウェア
LLMChain・RetrievalQAChain(langchain/chains) @langchain/classic のchains
langchain/llms・langchain/chat_models のモデル 各プロバイダのパッケージ
langchain/prompts などの基本部品 @langchain/core

旧チェーンを残したまま動かすだけなら@langchain/classicを入れてimportを書き換えれば足りますが、新しく書く処理はLCELかcreateAgentで書いてください。v1で変わった点の全体像(ミドルウェアの種類や構造化出力の設計)はLangChain v1.0の解説記事で扱っています。

LangChain.jsを使わないほうがよい場面

LangChain.jsは、ツール・履歴・構造化出力・ミドルウェアをまとめて扱えるのが強みです。逆にいえば、それらを使わない用途では依存の重さだけが残ります。

  • Next.jsのチャット画面にストリーミング表示を付けたいだけ:UI側のフックまで揃うVercel AI SDKのほうが短く書けます。LangChainのエージェントをAI SDKの画面につなぐ方法も同記事にあります。
  • 分岐・並列・長時間の中断と再開を細かく設計したい:createAgentの外側でLangGraphを直接書きます。手順はLangGraphでAIエージェントを実装する手順、両者の関係はLangChainとLangGraphの違いを参照してください。
  • TypeScriptで、ワークフローや評価まで1つのフレームワークで揃えたい:Mastraも候補になります。

本番に出す前には、どのツールが何回呼ばれたかを追えるようにしておきます。トレースの送り方はLangSmithの設定と料金で解説しています。業務システムへの組み込みや設計から相談したい場合は、一創のAIエージェント開発でも対応しています。

よくある質問

LangChain.jsの公式GitHubとドキュメントはどこですか?

ソースコードはgithub.com/langchain-ai/langchainjs、ドキュメントはdocs.langchain.com/oss/javascript、APIリファレンスはreference.langchain.com/javascriptです。旧ドキュメントのjs.langchain.comはこのサイトへ移っています。

npmではどのパッケージを入れればよいですか?

langchainと@langchain/coreに、使うモデルのプロバイダのパッケージ(@langchain/openaiや@langchain/anthropic)とzodを足します。@langchain/communityや@langchain/classicは、それに含まれる統合や旧チェーンを使うときだけ入れます。

TypeScriptで使えますか?

使えます。型定義はパッケージに同梱されていて、別途@typesを入れる必要はありません。ツールの引数をzodで定義すると、処理本体の引数にも型が付きます。

ReactやNext.jsのフロントエンドから直接呼べますか?

ブラウザ向けのビルドはありますが、APIキーをブラウザに置くことになるため、モデルの呼び出しはNext.jsのRoute HandlerやServer Actionなどサーバー側で行います。

LangChain.jsのエージェントとLangGraph.jsの違いは何ですか?

createAgentはLangGraphの上に作られた、モデルとツールの往復を組み立て済みのエージェントです。LangGraph.jsは、その往復の流れ自体をノードとエッジで自分で設計するための基盤です。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

ほか 3 件の記事からもリンクされています。

資料請求

RELATED POSTS 関連記事

目次