mcpField
L
langchain-fundamentals

langchain-fundamentals

github.com/langchain-aiエージェントワークフロー1.3k17.6k 回インストール3 日前に更新
リポジトリ

langchain-fundamentals について

create_agent()、ツール、ミドルウェア、構造化出力を使った LangChain エージェント構築のガイドです。Python または TypeScript の開発者向けに、状態の永続化、人による承認、実行制限のパターンを解説します。

主な機能

  1. 01create_agent() によるエージェント作成
  2. 02Python と TypeScript でのツール定義
  3. 03承認やフックのためのミドルウェア
  4. 04構造化・検証済み出力
  5. 05チェックポイントによる会話状態の保持
  6. 06エージェント実行の再帰制限

活用シーン

  1. 01ツールを呼び出すエージェントの構築
  2. 02ツール利用への人による承認の追加
  3. 03呼び出しをまたぐ会話状態の保持
  4. 04エージェントの実行ループの制限

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/langchain-ai/langchain-skills --skill langchain-fundamentals

インストール後、Claude Code で/langchain-fundamentalsを入力して読み込まれたか確認します。

詳細

`create_agent()`、ミドルウェアパターン、`@tool` デコレーター/`tool()` 関数を使用して、本番環境向けのエージェントを構築します。LangChain エージェントを作成する場合は、必ず `create_agent()` を使用し、カスタムフローにはミドルウェアを使用してください。それ以外の方法はすべて古くなっています。

<create_agent>

create_agent を使ったエージェントの作成

エージェントの構築には create_agent() を使用することを推奨します。エージェントのループ、ツールの実行、状態管理を処理します。

エージェントの設定オプション
パラメーター用途例
model使用する LLM"anthropic:claude-sonnet-4-5" またはモデルインスタンス
toolsツールのリスト[search, calculator]
system_prompt / systemPromptエージェントへの指示"You are a helpful assistant"
checkpointer状態の永続化MemorySaver()
middleware処理フック[HumanInTheLoopMiddleware](Python)/[humanInTheLoopMiddleware({...})](TypeScript)
</create_agent>
from langchain.agents import create_agent
from langchain_core.tools import tool

@tool
def get_weather(location: str) -> str:
    """Get current weather for a location.

    Args:
        location: City name
    """
    return f"Weather in {location}: Sunny, 72F"

agent = create_agent(
    model="anthropic:claude-sonnet-4-5",
    tools=[get_weather],
    system_prompt="You are a helpful assistant."
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "What's the weather in Paris?"}]
})
print(result["messages"][-1].content)
import { createAgent } from "langchain";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
  async ({ location }) => `Weather in ${location}: Sunny, 72F`,
  {
    name: "get_weather",
    description: "Get current weather for a location.",
    schema: z.object({ location: z.string().describe("City name") }),
  }
);

const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5",
  tools: [getWeather],
  systemPrompt: "You are a helpful assistant.",
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "What's the weather in Paris?" }],
});
console.log(result.messages[result.messages.length - 1].content);
呼び出しをまたいで会話状態を保持するには、MemorySaver チェックポインターを追加します。
from langchain.agents import create_agent
from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()

agent = create_agent(
    model="anthropic:claude-sonnet-4-5",
    tools=[search],
    checkpointer=checkpointer,
)

config = {"configurable": {"thread_id": "user-123"}}
agent.invoke({"messages": [{"role": "user", "content": "My name is Alice"}]}, config=config)
result = agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, config=config)
# Agent remembers: "Your name is Alice"
呼び出しをまたいで会話状態を保持するには、MemorySaver チェックポインターを追加します。
import { createAgent } from "langchain";
import { MemorySaver } from "@langchain/langgraph";

const checkpointer = new MemorySaver();

const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5",
  tools: [search],
  checkpointer,
});

const config = { configurable: { thread_id: "user-123" } };
await agent.invoke({ messages: [{ role: "user", content: "My name is Alice" }] }, config);
const result = await agent.invoke({ messages: [{ role: "user", content: "What's my name?" }] }, config);
// Agent remembers: "Your name is Alice"
## ツールの定義

ツールはエージェントが呼び出せる関数です。@tool デコレーター(Python)または tool() 関数(TypeScript)を使用します。

from langchain_core.tools import tool

@tool
def add(a: float, b: float) -> float:
    """Add two numbers.

    Args:
        a: First number
        b: Second number
    """
    return a + b
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const add = tool(
  async ({ a, b }) => a + b,
  {
    name: "add",
    description: "Add two numbers.",
    schema: z.object({
      a: z.number().describe("First number"),
      b: z.number().describe("Second number"),
    }),
  }
);
## エージェント制御のためのミドルウェア

ミドルウェアはエージェントのループに介入し、人間による承認、エラー処理、ログ記録などを追加します。本番環境向けのエージェントでは、ミドルウェアを深く理解することが不可欠です。承認ワークフローには HumanInTheLoopMiddleware(Python)/humanInTheLoopMiddleware(TypeScript)を、カスタムフックには @wrap_tool_call(Python)/createMiddleware(TypeScript)を使用してください。

主なインポート:

from langchain.agents.middleware import HumanInTheLoopMiddleware, wrap_tool_call
import { humanInTheLoopMiddleware, createMiddleware } from "langchain";

主なパターン:

  • HITL:middleware=[HumanInTheLoopMiddleware(interrupt_on={"dangerous_tool": True})] — checkpointer と thread_id が必要です
  • 割り込み後の再開:agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), config=config)
  • カスタムミドルウェア:@wrap_tool_call デコレーター(Python)または createMiddleware({ wrapToolCall: ... })(TypeScript)

<structured_output>

構造化出力

response_format または with_structured_output() を使用して、エージェントから型付けされ、検証済みの応答を取得します。

from langchain.agents import create_agent
from pydantic import BaseModel, Field

class ContactInfo(BaseModel):
    name: str
    email: str
    phone: str = Field(description="Phone number with area code")

# Option 1: Agent with structured output
agent = create_agent(model="gpt-4.1", tools=[search], response_format=ContactInfo)
result = agent.invoke({"messages": [{"role": "user", "content": "Find contact for John"}]})
print(result["structured_response"])  # ContactInfo(name='John', ...)

# Option 2: Model-level structured output (no agent needed)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4.1")
structured_model = model.with_structured_output(ContactInfo)
response = structured_model.invoke("Extract: John, [email protected], 555-1234")
# ContactInfo(name='John', email='[email protected]', phone='555-1234')
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

const ContactInfo = z.object({
  name: z.string(),
  email: z.string().email(),
  phone: z.string().describe("Phone number with area code"),
});

// Model-level structured output
const model = new ChatOpenAI({ model: "gpt-4.1" });
const structuredModel = model.withStructuredOutput(ContactInfo);
const response = await structuredModel.invoke("Extract: John, [email protected], 555-1234");
// { name: 'John', email: '[email protected]', phone: '555-1234' }

<model_config>

モデルの設定

create_agent では、モデル文字列("anthropic:claude-sonnet-4-5"、"openai:gpt-4.1")またはカスタム設定用のモデルインスタンスを指定できます。

from langchain_anthropic import ChatAnthropic
agent = create_agent(model=ChatAnthropic(model="claude-sonnet-4-5", temperature=0), tools=[...])

</model_config>

明確な説明を記述すると、エージェントが各ツールをいつ使用すべきか判断しやすくなります。
# WRONG: Vague or missing description
@tool
def bad_tool(input: str) -> str:
    """Does stuff."""
    return "result"

# CORRECT: Clear, specific description with Args
@tool
def search(query: str) -> str:
    """Search the web for current information about a topic.

    Use this when you need recent data or facts.

    Args:
        query: The search query (2-10 words recommended)
    """
    return web_search(query)
明確な説明を記述すると、エージェントが各ツールをいつ使用すべきか判断しやすくなります。
// WRONG: Vague description
const badTool = tool(async ({ input }) => "result", {
  name: "bad_tool",
  description: "Does stuff.", // Too vague!
  schema: z.object({ input: z.string() }),
});

// CORRECT: Clear, specific description
const search = tool(async ({ query }) => webSearch(query), {
  name: "search",
  description: "Search the web for current information about a topic. Use this when you need recent data or facts.",
  schema: z.object({
    query: z.string().describe("The search query (2-10 words recommended)"),
  }),
});
呼び出しをまたいで会話のメモリを保持するには、チェックポインターと thread_id を追加します。
# WRONG: No persistence - agent forgets between calls
agent = create_agent(model="anthropic:claude-sonnet-4-5", tools=[search])
agent.invoke({"messages": [{"role": "user", "content": "I'm Bob"}]})
agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]})
# Agent doesn't remember!

# CORRECT: Add checkpointer and thread_id
from langgraph.checkpoint.memory import MemorySaver

agent = create_agent(
    model="anthropic:claude-sonnet-4-5",
    tools=[search],
    checkpointer=MemorySaver(),
)
config = {"configurable": {"thread_id": "session-1"}}
agent.invoke({"messages": [{"role": "user", "content": "I'm Bob"}]}, config=config)
agent.invoke({"messages": [{"role": "user", "content": "What's my name?"}]}, config=config)
# Agent remembers: "Your name is Bob"
呼び出しをまたいで会話のメモリを保持するには、チェックポインターと thread_id を追加します。
// WRONG: No persistence
const agent = createAgent({ model: "anthropic:claude-sonnet-4-5", tools: [search] });
await agent.invoke({ messages: [{ role: "user", content: "I'm Bob" }] });
await agent.invoke({ messages: [{ role: "user", content: "What's my name?" }] });
// Agent doesn't remember!

// CORRECT: Add checkpointer and thread_id
import { MemorySaver } from "@langchain/langgraph";

const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5",
  tools: [search],
  checkpointer: new MemorySaver(),
});
const config = { configurable: { thread_id: "session-1" } };
await agent.invoke({ messages: [{ role: "user", content: "I'm Bob" }] }, config);
await agent.invoke({ messages: [{ role: "user", content: "What's my name?" }] }, config);
// Agent remembers: "Your name is Bob"
無制限に続くエージェントのループを防ぐには、invoke の設定で recursion_limit を指定します。
# WRONG: No iteration limit - could loop forever
result = agent.invoke({"messages": [("user", "Do research")]})

# CORRECT: Set recursion_limit in config
result = agent.invoke(
    {"messages": [("user", "Do research")]},
    config={"recursion_limit": 10},  # Stop after 10 steps
)
無制限に続くエージェントのループを防ぐには、invoke の設定で recursionLimit を指定します。
// WRONG: No iteration limit
const result = await agent.invoke({ messages: [["user", "Do research"]] });

// CORRECT: Set recursionLimit in config
const result = await agent.invoke(
  { messages: [["user", "Do research"]] },
  { recursionLimit: 10 }, // Stop after 10 steps
);
result.content に直接アクセスするのではなく、結果から messages 配列にアクセスします。
# WRONG: Trying to access result.content directly
result = agent.invoke({"messages": [{"role": "user", "content": "Hello"}]})
print(result.content)  # AttributeError!

# CORRECT: Access messages from result dict
result = agent.invoke({"messages": [{"role": "user", "content": "Hello"}]})
print(result["messages"][-1].content)  # Last message content
result.content に直接アクセスするのではなく、結果から messages 配列にアクセスします。
// WRONG: Trying to access result.content directly
const result = await agent.invoke({ messages: [{ role: "user", content: "Hello" }] });
console.log(result.content); // undefined!

// CORRECT: Access messages from result object
const result = await agent.invoke({ messages: [{ role: "user", content: "Hello" }] });
console.log(result.messages[result.messages.length - 1].content); // Last message content

コメント