langchain-middleware
bởi langchain-ai
Phê duyệt có sự tham gia của con người, middleware tùy chỉnh và các mẫu đầu ra có cấu trúc cho các tác nhân LangChain. HumanInTheLoopMiddleware tạm dừng thực thi trước các lệnh gọi công cụ nguy hiểm, cho phép con người phê duyệt, chỉnh sửa đối số hoặc từ chối kèm phản hồi. Các chính sách ngắt theo từng công cụ cho phép bạn cấu hình các quy tắc phê duyệt khác nhau dựa trên mức độ rủi ro; yêu cầu một checkpointer và thread_id để duy trì trạng thái. Mẫu tiếp tục lệnh tiếp tục thực thi sau các quyết định của con người, hỗ trợ chỉnh sửa đối số công cụ...
npx skills add https://github.com/langchain-ai/langchain-skills --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.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"]},
}
)
],
)
Set up an agent with HITL that pauses before sending emails for human approval.
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.
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 [email protected]"}]
}, 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
)
Run the agent, detect an interrupt, then resume execution after human approval.
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 [email protected]" }]
}, 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.
# 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": "[email protected]", # Fixed email
"subject": "Project Meeting - Updated",
"body": "...",
},
},
}]
}),
config=config
)
Edit the tool arguments before approving when the original values need correction.
// 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: "[email protected]", // Fixed email
subject: "Project Meeting - Updated",
body: "...",
},
},
}]
}
}),
config
);
Reject a tool call and provide feedback explaining why it was rejected.
# 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.
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)— callhandler(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. ReturnNoneor a dict of state updates.
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
# 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({...})]
)
HITL requires a checkpointer to persist state.
// 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.
# WRONG
agent.invoke(input) # No config!
# CORRECT
agent.invoke(input, config={"configurable": {"thread_id": "user-123"}})
Use Command class to resume execution after an interrupt.
# 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.
// WRONG
await agent.invoke({ resume: { decisions: [...] } });
// CORRECT
import { Command } from "@langchain/langgraph";
await agent.invoke(new Command({ resume: { decisions: [{ type: "approve" }] } }), config);
Thêm skills từ langchain-ai
arxiv-search
langchain-ai
Tìm kiếm bản in trước và bài báo học thuật trên arXiv theo chủ đề có truy xuất tóm tắt. Tìm kiếm dựa trên truy vấn trong các lĩnh vực vật lý, toán học, khoa học máy tính, sinh học, thống kê và các lĩnh vực liên quan. Giới hạn kết quả có thể cấu hình (mặc định 10 bài báo) với kết quả được sắp xếp theo mức độ liên quan. Trả về tiêu đề và tóm tắt cho mỗi bài báo phù hợp. Yêu cầu gói Python arxiv; cài đặt qua pip nếu chưa có.
official
blog-post
langchain-ai
Viết bài blog dài với phân công nghiên cứu, mẫu nội dung có cấu trúc và ảnh bìa do AI tạo. Phân công nghiên cứu cho các tác nhân phụ trước khi viết, lưu trữ kết quả dưới dạng markdown để tham khảo và ngữ cảnh. Áp dụng cấu trúc bài viết năm phần: mở đầu thu hút, bối cảnh, nội dung chính (3–5 phần), ứng dụng thực tế và kết luận kèm lời kêu gọi hành động. Tạo ảnh bìa tối ưu SEO bằng các gợi ý chi tiết về chủ đề, phong cách, bố cục, màu sắc và ánh sáng. Xuất bài viết đến...
official
code-review
langchain-ai
Thực hiện đánh giá mã nguồn có cấu trúc đối với các thay đổi, kiểm tra tính chính xác, phong cách, bài kiểm tra và các vấn đề tiềm ẩn.
official
coding-prefs
langchain-ai
Đọc sở thích lập trình của người dùng từ /memory/coding-prefs.md trước khi đưa ra các quyết định về phong cách không tầm thường, và thêm các sở thích mới khi người dùng đưa ra…
official
competitor-analysis
langchain-ai
Khi được yêu cầu phân tích đối thủ cạnh tranh:
official
cudf-analytics
langchain-ai
Sử dụng để phân tích dữ liệu tăng tốc GPU trên các tập dữ liệu, CSV hoặc dữ liệu dạng bảng bằng NVIDIA cuDF. Kích hoạt khi các tác vụ liên quan đến tổng hợp groupby, thống kê…
official
cuml-machine-learning
langchain-ai
Sử dụng cho học máy tăng tốc GPU trên dữ liệu dạng bảng với NVIDIA cuML. Kích hoạt khi tác vụ liên quan đến phân loại, hồi quy, phân cụm, giảm chiều…
official
data-visualization
langchain-ai
Sử dụng để tạo biểu đồ chất lượng xuất bản và tóm tắt phân tích nhiều bảng. Kích hoạt khi nhiệm vụ liên quan đến trực quan hóa dữ liệu, vẽ kết quả, tạo…
official