langchain-middleware

PANGGIL KETERAMPILAN INI ketika Anda memerlukan persetujuan manusia dalam proses, middleware kustom, atau keluaran terstruktur. Mencakup HumanInTheLoopMiddleware untuk persetujuan manusia terhadap…

npx skills add https://github.com/langchain-ai/skills-benchmarks --skill langchain-middleware
Middleware patterns for production LangChain agents:
  • HumanInTheLoopMiddleware / humanInTheLoopMiddleware: Pause before dangerous tool calls for human approval
  • Custom middleware: Intercept tool calls for error handling, logging, retry logic
  • Command resume: Continue execution after human decisions (approve, edit, reject)

Requirements: Checkpointer + thread_id config for all HITL workflows.


Human-in-the-Loop

Set up an agent with HITL middleware that pauses before sending emails for approval. ```python from langchain.agents import create_agent from langchain.agents.middleware import HumanInTheLoopMiddleware from langgraph.checkpoint.memory import MemorySaver from langchain.tools import tool

@tool def send_email(to: str, subject: str, body: str) -> str: """Send an email.""" return f"Email sent to {to}"

agent = create_agent( model="gpt-4.1", tools=[send_email], checkpointer=MemorySaver(), # Required for HITL middleware=[ HumanInTheLoopMiddleware( interrupt_on={ "send_email": {"allowed_decisions": ["approve", "edit", "reject"]}, } ) ], )

</python>
<typescript>
Set up an agent with HITL that pauses before sending emails for human approval.
```typescript
import { createAgent, humanInTheLoopMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const sendEmail = tool(
  async ({ to, subject, body }) => `Email sent to ${to}`,
  {
    name: "send_email",
    description: "Send an email",
    schema: z.object({ to: z.string(), subject: z.string(), body: z.string() }),
  }
);

const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5",
  tools: [sendEmail],
  checkpointer: new MemorySaver(),
  middleware: [
    humanInTheLoopMiddleware({
      interruptOn: { send_email: { allowedDecisions: ["approve", "edit", "reject"] } },
    }),
  ],
});
Run the agent, detect an interrupt, then resume execution after human approval. ```python from langgraph.types import Command

config = {"configurable": {"thread_id": "session-1"}}

Step 1: Agent runs until it needs to call tool

result1 = agent.invoke({ "messages": [{"role": "user", "content": "Send email to john@example.com"}] }, config=config)

Check for interrupt

if "interrupt" in result1: print(f"Waiting for approval: {result1['interrupt']}")

Step 2: Human approves

result2 = agent.invoke( Command(resume={"decisions": [{"type": "approve"}]}), config=config )

</python>
<typescript>
Run the agent, detect an interrupt, then resume execution after human approval.
```typescript
import { Command } from "@langchain/langgraph";

const config = { configurable: { thread_id: "session-1" } };

// Step 1: Agent runs until it needs to call tool
const result1 = await agent.invoke({
  messages: [{ role: "user", content: "Send email to john@example.com" }]
}, config);

// Check for interrupt
if (result1.__interrupt__) {
  console.log(`Waiting for approval: ${result1.__interrupt__}`);
}

// Step 2: Human approves
const result2 = await agent.invoke(
  new Command({ resume: { decisions: [{ type: "approve" }] } }),
  config
);
Edit the tool arguments before approving when the original values need correction. ```python # Human edits the arguments — edited_action must include name + args result2 = agent.invoke( Command(resume={ "decisions": [{ "type": "edit", "edited_action": { "name": "send_email", "args": { "to": "alice@company.com", # Fixed email "subject": "Project Meeting - Updated", "body": "...", }, }, }] }), config=config ) ``` Edit the tool arguments before approving when the original values need correction. ```typescript // Human edits the arguments — editedAction must include name + args const result2 = await agent.invoke( new Command({ resume: { decisions: [{ type: "edit", editedAction: { name: "send_email", args: { to: "alice@company.com", // Fixed email subject: "Project Meeting - Updated", body: "...", }, }, }] } }), config ); ``` Reject a tool call and provide feedback explaining why it was rejected. ```python # Human rejects result2 = agent.invoke( Command(resume={ "decisions": [{ "type": "reject", "feedback": "Cannot delete customer data without manager approval", }] }), config=config ) ``` Configure different HITL policies for each tool based on risk level. ```python agent = create_agent( model="gpt-4.1", tools=[send_email, read_email, delete_email], checkpointer=MemorySaver(), middleware=[ HumanInTheLoopMiddleware( interrupt_on={ "send_email": {"allowed_decisions": ["approve", "edit", "reject"]}, "delete_email": {"allowed_decisions": ["approve", "reject"]}, # No edit "read_email": False, # No HITL for reading } ) ], ) ``` ### What You CAN Configure
  • Which tools require approval (per-tool policies)
  • Allowed decisions per tool (approve, edit, reject)
  • Custom middleware hooks: before_model, after_model, wrap_tool_call, before_agent, after_agent
  • Tool-specific middleware (apply only to certain tools)

Custom Middleware Hooks

Six decorator hooks are available. Two patterns:

  • Wrap hooks (wrap_tool_call, wrap_model_call): (request, handler) — call handler(request) to proceed, or return early to short-circuit.
  • Before/after hooks (before_model, after_model, before_agent, after_agent): (state, runtime) — inspect or modify state. Return None or a dict of state updates.
`@wrap_tool_call` intercepts tool execution. **Do NOT use `yield`** — it creates a generator and causes `NotImplementedError`.
from langchain.agents.middleware import wrap_tool_call

@wrap_tool_call
def retry_middleware(request, handler):
    for attempt in range(3):
        try:
            return handler(request)
        except Exception:
            if attempt == 2:
                raise

@wrap_tool_call
def guard_middleware(request, handler):
    if request.tool_call["name"] == "dangerous_tool":
        return "This tool is disabled"  # short-circuit
    return handler(request)
`createMiddleware({ wrapToolCall })` intercepts tool execution.
import { createMiddleware } from "langchain";

const retryMiddleware = createMiddleware({
  wrapToolCall: async (request, handler) => {
    for (let attempt = 0; attempt < 3; attempt++) {
      try { return await handler(request); }
      catch (e) { if (attempt === 2) throw e; }
    }
  },
});
`before_model` / `after_model` / `before_agent` / `after_agent` all share `(state, runtime)` signature.
from langchain.agents.middleware import before_model, after_model

@before_model
def log_calls(state, runtime):
    print(f"Calling model with {len(state['messages'])} messages")

@after_model
def check_output(state, runtime):
    print(f"Model responded")
All before/after hooks share the same `(state, runtime)` signature via `createMiddleware`.
import { createMiddleware } from "langchain";

const loggingMiddleware = createMiddleware({
  beforeModel: (state, runtime) => {
    console.log(`Calling model with ${state.messages.length} messages`);
  },
  afterModel: (state, runtime) => {
    console.log("Model responded");
  },
});
### What You CANNOT Configure
  • Interrupt after tool execution (must be before)
  • Skip checkpointer requirement for HITL
HITL middleware requires a checkpointer to persist state. ```python # WRONG agent = create_agent(model="gpt-4.1", tools=[send_email], middleware=[HumanInTheLoopMiddleware({...})])

CORRECT

agent = create_agent( model="gpt-4.1", tools=[send_email], checkpointer=MemorySaver(), # Required middleware=[HumanInTheLoopMiddleware({...})] )

</python>
<typescript>
HITL requires a checkpointer to persist state.
```typescript
// WRONG: No checkpointer
const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5", tools: [sendEmail],
  middleware: [humanInTheLoopMiddleware({ interruptOn: { send_email: true } })],
});

// CORRECT: Add checkpointer
const agent = createAgent({
  model: "anthropic:claude-sonnet-4-5", tools: [sendEmail],
  checkpointer: new MemorySaver(),
  middleware: [humanInTheLoopMiddleware({ interruptOn: { send_email: true } })],
});
Always provide thread_id when using HITL to track conversation state. ```python # WRONG agent.invoke(input) # No config!

CORRECT

agent.invoke(input, config={"configurable": {"thread_id": "user-123"}})

</python>
</fix-no-thread-id>

<fix-wrong-resume-syntax>
<python>
Use Command class to resume execution after an interrupt.
```python
# WRONG
agent.invoke({"resume": {"decisions": [...]}})

# CORRECT
from langgraph.types import Command
agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), config=config)
Use Command class to resume execution after an interrupt. ```typescript // WRONG await agent.invoke({ resume: { decisions: [...] } });

// CORRECT import { Command } from "@langchain/langgraph"; await agent.invoke(new Command({ resume: { decisions: [{ type: "approve" }] } }), config);

</typescript>
</fix-wrong-resume-syntax>

Lebih banyak skill dari langchain-ai

langgraph-docs
langchain-ai
Mengakses dokumentasi LangGraph untuk membangun agen stateful dan alur kerja multi-agen. Mengambil dokumentasi resmi LangGraph Python yang mencakup mesin state, desain agen berbasis grafik, dan pola human-in-the-loop. Memprioritaskan dokumentasi yang relevan berdasarkan jenis kueri: panduan implementasi untuk pertanyaan cara, halaman konsep untuk teori, tutorial untuk contoh ujung ke ujung, dan referensi API untuk detail teknis. Secara otomatis memilih 2–4 URL dokumentasi yang paling relevan dan mengambil kontennya untuk menjawab...
official
langgraph-human-in-the-loop
langchain-ai
Jeda eksekusi graf untuk peninjauan, persetujuan, atau validasi manusia, lalu lanjutkan dengan masukan mereka. Membutuhkan tiga komponen: checkpointer (InMemorySaver atau PostgresSaver), ID thread dalam konfigurasi, dan payload interupsi yang dapat diserialisasi JSON. interrupt(value) menjeda dan menampilkan data; Command(resume=value) melanjutkan dan mengembalikan nilai tersebut ke node yang dijeda. Semua kode sebelum interrupt() akan dieksekusi ulang saat melanjutkan, sehingga efek samping harus idempoten (gunakan upsert, bukan insert). Mendukung alur kerja persetujuan,...
official
web-research
langchain-ai
Gunakan keterampilan ini untuk permintaan yang terkait dengan riset web; ini menyediakan pendekatan terstruktur untuk melakukan riset web yang komprehensif.
official
langchain-oss-primer
langchain-ai
SELALU MULAI DI SINI untuk proyek pembuatan agen LangChain, Deep Agents, atau LangGraph apa pun. Titik awal yang diperlukan sebelum memilih keterampilan lain atau menulis apa pun…
official
skill-creator
langchain-ai
Panduan untuk membuat skill yang efektif guna memperluas kemampuan agen dengan pengetahuan khusus, alur kerja, atau integrasi alat. Gunakan skill ini ketika pengguna…
official
social-media
langchain-ai
Menyusun draf posting media sosial khusus platform dengan konten berbasis riset dan gambar pendamping yang dihasilkan. Mendukung posting LinkedIn (1.300 karakter dengan nada profesional) dan utas Twitter/X (280 karakter per tweet dengan format 1/🧵). Memerlukan delegasi riset ke subagen sebelum menulis, kemudian membaca temuan untuk memastikan akurasi dan relevansi. Menghasilkan gambar sosial yang menarik secara otomatis menggunakan alat generate_social_image dengan komposisi tebal dan kontras tinggi yang dioptimalkan untuk ukuran kecil...
official
deep-agents-memory
langchain-ai
Backend memori dan file yang dapat dipasang untuk Deep Agents dengan opsi perutean sementara, persisten, dan hibrida. Empat jenis backend: StateBackend (berlaku dalam thread, sementara), StoreBackend (persisten lintas sesi), FilesystemBackend (akses disk nyata untuk pengembangan lokal), dan CompositeBackend (merutekan jalur berbeda ke backend berbeda). FilesystemMiddleware menyediakan enam alat operasi file: ls, read_file, write_file, edit_file, glob, grep. CompositeBackend menggunakan pencocokan prefiks terpanjang untuk merutekan...
official
deep-agents-orchestration
langchain-ai
We need to translate the given English text into Indonesian. The text describes an agent skill for orchestrating subagents, planning tasks, requiring human approval, delegating work, etc. We must preserve product names, protocol names, URLs, numbers, technical terms. The name "deep-agents-orchestration" is not in the text, so we don't include it. We translate only the text inside <text>. No extra commentary, labels, etc. Let's translate step by step: "Orchestrate subagents, plan multi-step tasks, and require human approval for sensitive operations." -> "Orkestrasi subagen, rencanakan tugas multi-langkah, dan minta persetujuan manusia untuk operasi sensitif." "Delegate work to specialized subagents via the task tool; custom subagents support isolated tool sets and system prompts, while the default "general-purpose" subagent inherits main agent configuration" -> "Delegasikan pekerjaan ke subagen khusus melalui alat tugas; subagen kustom mendukung set alat dan prompt sistem yang terisolasi, s
official