21-Middleware-Runtime-Guardrails-HITL官方深读
核对日期:2026-05-18
官方资料:Middleware overview https://docs.langchain.com/oss/javascript/langchain/middleware/overview;Runtime https://docs.langchain.com/oss/javascript/langchain/runtime;Guardrails https://docs.langchain.com/oss/javascript/langchain/guardrails;Human-in-the-loop https://docs.langchain.com/oss/javascript/langchain/human-in-the-loop。
官方概念
Runtime context 是把数据穿过 Agent 的方式。官方文档强调不要依赖全局状态,而是把数据库连接、用户会话、配置、权限等附加到 context,再在 tools 和 middleware 中读取。创建 Agent 时可用 contextSchema 定义结构,调用时通过 context 传入。
Middleware 是 LangChain 高阶用法的核心接口。它可以在模型调用、工具调用、Agent 前后插入逻辑,用于动态 prompt、动态工具、错误处理、日志、上下文压缩和安全拦截。
Guardrails 是安全和合规检查,可以在 Agent 开始前、完成后、模型/工具调用周围执行。官方内置能力包括 PII 处理和 HITL;也支持自定义 middleware。
HITL middleware 可以对敏感 tool call 发出 interrupt,保存 graph state,等待人工 approve/edit/reject/respond 后继续。
机制
Runtime 深读
| 官方能力 | 工程用途 |
|---|---|
contextSchema | 对运行时上下文做类型和结构校验 |
runtime.context | 工具和 middleware 读取 user id、role、tenant、环境 |
runtime.store | 读取/写入长期记忆 |
runtime.executionInfo | 访问 thread id、run id,做审计关联 |
runtime.serverInfo | 在 LangGraph Server 上访问 assistant/user 元数据 |
runtime context 不应直接拼进 prompt。它首先是控制面数据:权限、连接、配置、审计身份。只有经过策略筛选的内容才能进入 model context。
Middleware 深读
| Hook / 模式 | 能做什么 | 示例 |
|---|---|---|
beforeModel | 调模型前修改 state/messages 或拦截 | trim messages、注入 system prompt |
afterModel | 调模型后检查输出 | 输出质量、结构检查 |
wrapModelCall | 包裹模型调用 | 动态模型、动态工具、临时上下文 |
wrapToolCall | 包裹工具调用 | 错误处理、重试、审计 |
beforeAgent | Agent 开始前拦截 | 认证、限流、内容过滤 |
afterAgent | Agent 完成后检查 | 安全审查、合规扫描 |
middleware 顺序很重要。建议按“认证/输入过滤 -> 上下文处理 -> 模型/工具策略 -> 输出过滤 -> 日志”排序。
Guardrails 深读
官方 Guardrails 文档区分 deterministic 和 model-based 两类:
| 类型 | 优点 | 缺点 | 适合 |
|---|---|---|---|
| Deterministic | 快、可预测、成本低 | 对语义变体弱 | PII regex、关键词、权限、格式 |
| Model-based | 能理解语义 | 慢、贵、也可能错 | 安全分类、质量判断、复杂合规 |
官方 PII middleware 支持 email、credit card、IP、MAC、URL 等类型,也支持自定义 detector;策略包括 redact、mask、hash、block。工程上要按 applyToInput / applyToOutput / applyToToolResults 分层,而不是只在输入层做一次。
HITL 深读
官方 HITL middleware 的核心点:
| 能力 | 说明 |
|---|---|
interruptOn | 配置哪些工具需要审批 |
| checkpointer | 必须保存状态,才能暂停/恢复 |
| decision: approve | 原样执行 |
| decision: edit | 修改参数后执行 |
| decision: reject | 拒绝并把反馈加入对话 |
| decision: respond | 跳过工具,把人工回复作为工具结果 |
| 多工具中断 | 多个 action 需要按顺序提供多个 decision |
| streaming + interrupt | 可边流式输出边检测中断 |
HITL 不是“前端 confirm 弹窗”。它是持久化工作流的一部分,必须绑定 thread id、工具名、参数、审批人、审批时间和可恢复状态。
TypeScript 落地模板
const contextSchema = z.object({
userId: z.string(),
role: z.enum(["viewer", "operator", "admin"]),
tenantId: z.string(),
});
const authAndAudit = createMiddleware({
name: "AuthAndAudit",
contextSchema,
beforeAgent: {
hook: (state, runtime) => {
if (!runtime.context.userId) {
throw new Error("Authentication required");
}
return;
},
},
wrapToolCall: async (request, handler) => {
const startedAt = Date.now();
try {
const result = await handler(request);
// 写审计日志:tool name、user id、latency、ok
return result;
} catch (error) {
// 统一转成用户可读错误,内部错误进入 trace
throw error;
} finally {
void startedAt;
}
},
});
Python 差异
Python 和 TypeScript 的 HITL 都依赖 LangGraph 的 interrupt/resume 思路,但 API 名称和类型不同。Python 示例里常见 interrupt_on,TypeScript 文档中使用 interruptOn。跨语言项目必须统一审批决策类型和审计字段。
工程边界
- Runtime context 不能替代鉴权服务,只是传递已验证身份和配置。
- Middleware 不能变成隐藏业务逻辑;每个 middleware 都要有测试。
- Guardrails 应分层,不要指望单个模型安全判断解决所有风险。
- HITL 必须持久化,不持久化的审批无法安全恢复。
- Edit 决策要保守,修改工具参数可能让 Agent 重新规划并重复执行。
常见反模式
| 反模式 | 后果 |
|---|---|
| 把权限写进 system prompt | prompt injection 可绕过,且不可审计 |
| middleware 里悄悄改用户输入 | eval 难复现 |
| PII 只过滤输入不检查工具返回 | 工具结果仍可能泄露 |
| HITL 无 thread id | 中断后无法恢复正确会话 |
| 审批不绑定参数 hash | 审批后参数被替换仍执行 |
练习任务
- 给多工具任务 Agent 设计
contextSchema,至少包含userId、role、tenantId。 - 增加一个 deterministic guardrail:检测 API key 形态时 block。
- 增加一个 HITL 审批记录结构,包含 toolInputHash。
- 为 middleware 顺序写一份测试:认证失败时不能进入工具调用。