LangChain エージェント実装ガイド 2026 — 本番運用までの技術解説

Surya Pratap
By Surya Pratap

2026年6月20日

14分で読めます

AI・テクノロジー
本番環境で稼働する LangChain エージェントLangChain エージェントHover to explore
エージェントはプロンプトではなくループです。観測し、推論し、実行する。完了するまでこれを繰り返します。

質問に答えるだけの LLM も役には立ちます。しかし 実際に動く LLM——データベースを検索し、Slack にメッセージを送り、返金処理を実行し、その結果を報告する——になると、それはプロダクトです。2026年、このループを組むための標準的な手段が LangChain エージェント です。本記事では、考え方から本番コードまでを一通り扱います。エージェントループ、ツールの書き方、メモリと状態、構造化出力、ストリーミング、エラー処理、そして r/LangChain、r/LocalLLaMA、X の開発者たちが「これは効く」「これは効かない」と語っている内容です。

本記事は LangChain v1 のエージェント公式ドキュメントLangGraph のドキュメント、および r/LangChain と X の @LangChainAI で繰り返し交わされている議論にもとづいています。

1. エージェントループ — 実行時に起きていること

どのモデルを使っても、LangChain エージェントは同じループを回します。

  1. 観測 — LLM が会話履歴と、利用可能なツールの一覧(名前、説明、引数の JSON スキーマ)を受け取ります。
  2. 推論 — ツールを呼ぶか、最終的な回答を返すかをモデルが判断します。最近のモデルは自由記述から JSON を取り出すのではなく、ネイティブの tool calling を使います。
  3. 実行 — ツール呼び出しが要求されると、LangChain がそれを実行し、結果を ToolMessage として会話に追加します。
  4. 反復 — モデルがツールを呼ばずに応答を返すか、設定した最大反復回数に達するまでループが続きます。

仕組みはこれだけです。差がつくのは、どんなツールを渡すか、ループをどう制約するか。フレームワークの内部に魔法があるわけではありません。

2. 15行で書く最初の LangChain エージェント

LangChain v1 の create_agent を使えば、定型コードはほとんど不要です。

from langchain.agents import create_agent
from langchain.tools import tool

@tool
def search_orders(order_id: str) -> str:
    """Look up an order by ID and return its current status."""
    # Replace with your real DB call
    return f"Order {order_id}: Shipped, arriving 2026-06-23."

@tool
def issue_refund(order_id: str, reason: str) -> str:
    """Issue a full refund for an order given a reason."""
    return f"Refund issued for order {order_id}. Reason: {reason}."

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search_orders, issue_refund],
    system_prompt=(
        "You are a helpful customer support agent. "
        "Always look up the order before issuing a refund."
    ),
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "Refund order #4821 — it arrived broken."}]
})
print(result["messages"][-1].content)

内部では、エージェントが search_orders("4821") を呼び、その結果を読み、続けて issue_refund("4821", "arrived broken") を呼び、最後に確認メッセージを返します。これがすべて invoke 1回のなかで完結し、手作業のオーケストレーションは要りません。

3. 良いツールを書く — 多くのエージェントが失敗する場所

エージェントの品質を最も左右するのは、モデル選びではなくツール設計です。モデルは関数名、docstring、引数スキーマだけを見てどのツールを呼ぶかを決めます。ここを外すと、どれだけプロンプトを工夫しても取り返せません。

実務で通用するルール

  • 1ツール1アクション。 search_and_refund() は罠です。分割してください。モデルが各ステップを独立して呼べる形にします。
  • docstring は人間ではなくモデルに向けて書く。 いつ 呼ぶのか、何を返すのか、満たすべき前提条件は何かを明記します(「注文の存在を確認してからのみ呼び出すこと」など)。
  • 文字列か単純な JSON を返す。 モデルは戻り値をテキストとして読みます。深くネストしたオブジェクトは混乱のもとで、一文の要約のほうがうまく機能します。
  • エラーはツールの外ではなく中で処理する。 例外を投げるのではなく "Error: order not found" を返します。そうすればエージェントは再試行するか、失敗の理由を説明できます。落ちることはありません。
from langchain.tools import tool
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    order_id: str = Field(description="The numeric order ID, e.g. '4821'")
    include_history: bool = Field(
        default=False,
        description="Set True to include the full shipment history"
    )

@tool(args_schema=SearchInput)
def search_orders(order_id: str, include_history: bool = False) -> str:
    """
    Look up an order's current status by order ID.
    Call this FIRST before any action that modifies the order.
    Returns: status string, or an error message if not found.
    """
    try:
        order = db.get_order(order_id)
        if not order:
            return f"Error: order {order_id} not found."
        base = f"Order {order_id}: {order.status}, ETA {order.eta}."
        if include_history:
            base += f" History: {order.shipment_history}"
        return base
    except Exception as e:
        return f"Error fetching order: {str(e)}"

4. メモリと永続的な状態

既定では agent.invoke() はステートレスです。処理が終わった瞬間、エージェントは直前のやり取りを忘れます。複数ターンの会話を成立させるには、状態を明示的に渡す必要があります。LangChain には2つのパターンがあります。

パターン A — LangGraph によるスレッド単位のチェックポイント(本番推奨)

from langchain.agents import create_agent
from langgraph.checkpoint.memory import MemorySaver

# MemorySaver keeps state in RAM; swap for PostgresSaver in production
checkpointer = MemorySaver()

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search_orders, issue_refund],
    checkpointer=checkpointer,
)

# Same thread_id = same conversation memory
config = {"configurable": {"thread_id": "user-123-session-456"}}

# Turn 1
agent.invoke(
    {"messages": [{"role": "user", "content": "What is the status of order 4821?"}]},
    config=config,
)

# Turn 2 — agent remembers turn 1
agent.invoke(
    {"messages": [{"role": "user", "content": "Go ahead and refund it."}]},
    config=config,
)

パターン B — 長い文脈のための要約

スレッドがモデルのコンテキストウィンドウを超えたら、SummarizationMiddleware を使います。モデル呼び出しのたびに、古いメッセージを累積要約へ自動的に圧縮します。逐語的な履歴は失われますが、意味は保たれます。サポートやアシスタント用途であれば、これで十分です。

LangGraph エージェントの状態グラフ既定でステートフルHover to explore
LangGraph のチェックポイント機構は、どんなエージェントも再開可能な複数ターンの会話に変えます。サーバーを再起動しても続きから再開できます。

5. 構造化出力 — 型付きデータを受け取る

自由記述の要約ではなく、検証済みのオブジェクトを返してほしい場面は少なくありません。response_format に Pydantic モデルを渡せば、同じループのなかで型付きオブジェクトが得られます。追加のパース処理は不要です。

from pydantic import BaseModel
from langchain.agents import create_agent

class SupportTicket(BaseModel):
    order_id: str
    issue_type: str          # "refund" | "late_delivery" | "wrong_item"
    recommended_action: str
    confidence: float        # 0.0 – 1.0

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search_orders],
    response_format=SupportTicket,
    system_prompt=(
        "Classify the customer's issue and recommend an action. "
        "Always search the order before classifying."
    ),
)

result = agent.invoke({"messages": [
    {"role": "user", "content": "My order 4821 never arrived — it's been 3 weeks."}
]})

ticket: SupportTicket = result["structured_response"]
print(ticket.issue_type)           # "late_delivery"
print(ticket.recommended_action)   # "escalate to carrier"
print(ticket.confidence)           # 0.92

6. ストリーミング — 体感速度を上げる

エージェント最大の UX 課題はレイテンシです。ループが終わるまで、ユーザーには何も表示されません。ストリーミングは、トークンとツールのイベントを発生順に見せることでこれを解決します。LangChain は2つのモードに対応しています。

# Mode 1 — stream final output tokens only
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "Status of order 4821?"}]},
    stream_mode="messages",
):
    if chunk[1].get("langgraph_node") == "agent":
        print(chunk[0].content, end="", flush=True)

# Mode 2 — stream every event (tool calls, results, tokens)
for event in agent.stream(
    {"messages": [{"role": "user", "content": "Status of order 4821?"}]},
    stream_mode="updates",
):
    kind = list(event.keys())[0]
    if kind == "tools":
        print(f"[tool] {event['tools']['messages'][0].name}")
    elif kind == "agent":
        for msg in event["agent"]["messages"]:
            print(msg.content, end="", flush=True)

実務では、「思考中」パネルを表示する UI なら stream_mode="updates" を使ってください。最終回答を待たずに、いまどのツールを呼んでいるかを提示できます。

7. 人間による承認 — 唯一省略できないガードレール

金銭を動かす、メッセージを送信する、データベースに書き込む。こうした操作ができるエージェントには、取り消せないアクションに対する人間の承認が必要です。LangChain v1 には2つの方法があります。単純なケースは middleware、細かく制御したい場合は LangGraph の interrupt です。

# Simple approach — middleware
from langchain.agents.middleware import HumanInTheLoopMiddleware

agent = create_agent(
    model="anthropic:claude-sonnet-4-6",
    tools=[search_orders, issue_refund, send_email],
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={"issue_refund": True, "send_email": True}
        )
    ],
)

# Advanced approach — LangGraph interrupt node
from langgraph.types import interrupt

def human_approval_node(state):
    last_tool_call = state["messages"][-1]
    # Pause execution and surface the tool call to your UI
    decision = interrupt({
        "tool": last_tool_call.name,
        "args": last_tool_call.tool_input,
        "prompt": "Approve this action?",
    })
    if decision["approved"]:
        return state  # continue
    # Inject a rejection message back into the graph
    return {"messages": [ToolMessage(content="Action rejected by user.", ...)]}

導入が速いのは middleware です。LangGraph の interrupt はより柔軟で、承認・却下だけでなく引数の変更もできます。クライアント案件では、まず middleware から始めるのが通例です。実行前にユーザーがツール呼び出しを編集する必要が出てきた段階で、interrupt に切り替えます。

8. ReAct と tool calling — いま使っているのはどちらか

ここを混同している開発者は多くいます。2022〜2023年の LangChain では ReAct(Reason + Act)が主流でした。モデルに「Thought / Action / Observation」という書式のスクラッチパッドをプレーンテキストで書かせ、それを LangChain が解析して呼び出し先を決めていました。動作はしましたが脆く、書式が少しでも崩れるとパーサーが壊れました。

2026年の create_agentネイティブ tool calling を使います。モデルは自由記述ではなく、API レスポンスとして構造化された呼び出しを返します。こちらのほうが信頼性が高く、プロンプトが二重にならないぶん低コストで、主要プロバイダーすべてが対応しています。ReAct はネイティブ tool calling に非対応のモデル向けの代替手段であり、標準の選択肢ではなくなりました。

項目ReAct(レガシー)ネイティブ tool calling(v1 既定)
形式自由記述のスクラッチパッド構造化された API レスポンス
信頼性書式に依存スキーマで検証
ツールの並列呼び出し不可可(対応環境の場合)
向いている用途tool calling 非対応のローカルモデルそれ以外のすべて

9. 本番投入前のチェックリスト

  1. LangSmith を初日から入れる。 LANGSMITH_API_KEYLANGSMITH_TRACING=true を設定します。invoke もツール呼び出しもトークンも、すべて自動でトレースされます。本番で障害が起きた深夜2時に、入れておいてよかったと思うはずです。
  2. max_iterations を設定する。 上限のないエージェントは、説明の悪いツール1つで無限ループに入ります。まず10から始め、通常のループ深度がわかったら下げてください。
  3. 破壊的なツールはすべて人間の承認で守る。 例外はありません。本番で返金を1件誤るコストは、エンジニア1週間分より高くつきます。
  4. まずツール単体でテストする。 各ツール関数をモック入力でユニットテストします。エージェントの挙動は予測しにくいものですが、ツールの正しさは予測できます。
  5. 評価セットを用意する。 LangSmith Datasets を使えば、実際の入力と期待される出力を記録し、モデル更新のたびに回帰評価を回せます。これがないと、バージョンを上げるたびに手探りになります。
  6. 本番では永続的な checkpointer を使う。 MemorySaver はローカル開発用です。公開前に PostgresSaverRedisSaver へ差し替えてください。そうしないとサーバー再起動で会話状態がすべて消えます。

10. 2026年、Reddit と X で語られていること

r/LangChain、r/LocalLLaMA、そして X の AI エンジニアリング界隈のスレッドを数か月分読み込むと、コミュニティの見解は次のような論点に収束します。

  • 「ツール設計が問題の80%だ」。 r/LangChain で最も支持されるデバッグ投稿は、ほぼ例外なく命名の悪いツールか曖昧な docstring の話であり、フレームワークのバグの話ではありません。名前を変えたら動いた、という報告が続きます。
  • 「print デバッグから LangSmith に移ったのが転機だった」。 デモから実プロダクトへ進んだ開発者が繰り返し口にする感想です。トレース画面を見れば、どのツール呼び出しがなぜ失敗したかが正確にわかります。
  • 「LangChain をやめて SDK を直接呼ぶようにした」。 これも定番の投稿です。特定プロバイダーに深く寄せているチームや、100ms を切るレイテンシを狙うチームで多く見られます。1〜3ステップの単純なパイプラインでは、フレームワークのオーバーヘッドは実在します。すべての用途に必要なわけではありません。
  • 「パッケージ分割(langchain / langchain-core / langgraph)はいまだに初学者を混乱させる」。 LangChain チーム自身も認めています。v1 で概念整理は進みましたが、最初のインストールでつまずく点は残っています。
  • 「並列ツール呼び出しでようやく複雑な検索が速くなった」。 これまで順番に呼び出していた開発者が、独立したツールを同時に発火させることでレイテンシを2〜3倍改善しています。

「2026年の問いは『LangChain を使うべきか』ではありません。『チェックポイント、可観測性、プロバイダー横断の tool calling が必要なのか。それともプロバイダーは1つ、ステップは3つで、直接呼び出しで足りるのか』です。フレームワークを選ぶ前に、自分がどちらの問題を抱えているかを見極めてください。」

11. LangChain エージェントを選ぶ基準

次のうち2つ以上が必要なら、LangChain エージェントが適しています。

  • プロバイダー間の可搬性(同じエージェントを Claude、GPT、Gemini でコード1行の変更で動かせる)
  • 一時停止と再開ができる、複数ターンの永続的な状態
  • 破壊的なアクションに対する人間の承認
  • 独自のログ実装なしで使えるトレースと評価
  • 再利用可能な middleware(個人情報のマスキング、要約、リトライ)

一方、次の場合はプロバイダーの SDK を直接使うほうが適しています。

  • 単一プロバイダーで、レイテンシが最優先の制約である場合(フレームワークのオーバーヘッドがあると <100ms は困難です)
  • 「エージェント」と呼んでいるものが実際には分岐のない2〜3ステップの直列処理である場合(グラフよりパイプラインのほうが単純です)
  • 抽象化について明確な方針があり、ループ全体を自分で制御したい場合

まとめ

2026年の LangChain エージェントは、もはやチュートリアル用のおもちゃではありません。create_agent、ネイティブ tool calling、チェックポイント付きの状態管理、構造化出力、ストリーミング、middleware。本番のエージェントに必要な範囲を、フレームワークが一通り押さえています。学習コストは確かにあります。特にツール設計と LangGraph のチェックポイントモデルは相応の理解が要ります。それでも得られるのは、観測でき、停止でき、人間に引き継げ、コードを書き換えずに別モデルへ移せるエージェントです。AI を使った MVP の土台として、これは強い出発点になります。

IdeaToMVP Academy

Want to build with AI — not just read about it?

4-week live cohort for founders. Learn to ship AI agents, scope MVPs, and automate your business — taught by the same team that writes these guides.

Explore the Academy →
Share this post :