自定义中间件
通过实现运行在 Agent 执行流程中特定节点的钩子函数,来构建自定义中间件。
钩子
中间件提供了两种风格的钩子来拦截 Agent 的执行:
在特定的执行节点按顺序运行。
环绕每次模型或工具调用运行。
节点风格钩子
在特定的执行节点按顺序运行。用于日志记录、校验和状态更新。
选择中间件所需的钩子。你可以在节点风格钩子和包裹风格钩子之间进行选择。
节点风格钩子在特定的执行节点运行:
| 钩子 | 运行契机 |
|---|---|
beforeAgent | Agent 启动前(每次调用运行一次) |
beforeModel | 每次模型调用前 |
afterModel | 每次模型响应后 |
afterAgent | Agent 完成后(每次调用运行一次) |
包裹风格钩子环绕每次调用运行,让你能够控制执行流程:
| 钩子 | 运行契机 |
|---|---|
wrapModelCall | 环绕每次模型调用 |
wrapToolCall | 环绕每次工具调用 |
示例:
import { createMiddleware, AIMessage } from "langchain";
const createMessageLimitMiddleware = (maxMessages: number = 50) => {
return createMiddleware({
name: "MessageLimitMiddleware",
beforeModel: {
canJumpTo: ["end"],
hook: (state) => {
if (state.messages.length === maxMessages) {
return {
messages: [new AIMessage("Conversation limit reached.")],
jumpTo: "end",
};
}
return;
}
},
afterModel: (state) => {
const lastMessage = state.messages[state.messages.length - 1];
console.log(`Model returned: ${lastMessage.content}`);
return;
},
});
};包裹风格钩子
拦截执行并控制处理函数(handler)的调用时机。用于重试、缓存和数据转换。
由你来决定处理函数是被调用零次(短路)、一次(正常流程)还是多次(重试逻辑)。
可用钩子:
wrapModelCall- 环绕每次模型调用wrapToolCall- 环绕每次工具调用
示例:
import { createMiddleware } from "langchain";
const createRetryMiddleware = (maxRetries: number = 3) => {
return createMiddleware({
name: "RetryMiddleware",
wrapModelCall: (request, handler) => {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return handler(request);
} catch (e) {
if (attempt === maxRetries - 1) {
throw e;
}
console.log(`Retry ${attempt + 1}/${maxRetries} after error: ${e}`);
}
}
throw new Error("Unreachable");
},
});
};状态更新
节点风格钩子和包裹风格钩子都可以更新 Agent 状态。两者的机制有所不同:
- 节点风格钩子(
beforeAgent、beforeModel、afterModel、afterAgent):直接返回一个字典(dict/对象)。该字典会通过图的 reducer 应用到 Agent 状态中。 - 包裹风格钩子(
wrapModelCall、wrapToolCall):对于模型调用,直接返回一个Command,以便在返回模型响应的同时注入状态更新。对于工具调用,同样直接返回一个Command。当需要根据模型或工具调用期间运行的逻辑来跟踪或更新状态时(例如摘要触发点、使用量元数据,或根据请求/响应计算出的自定义字段),请使用这些钩子。
节点风格钩子
从节点风格钩子中返回一个字典,以将更新合并到 Agent 状态中。字典的键映射到状态字段。
import { createMiddleware } from "langchain";
import * as z from "zod";
const trackingStateSchema = z.object({
modelCallCount: z.number().default(0),
});
const incrementAfterModel = createMiddleware({
name: "incrementAfterModel",
stateSchema: trackingStateSchema,
afterModel: (state) => {
return { modelCallCount: state.modelCallCount + 1 };
},
});包裹风格钩子
直接从 wrapModelCall 返回一个 Command,以从模型调用层注入状态更新:
import * as z from "zod";
import { createMiddleware } from "langchain";
import { Command } from "@langchain/langgraph";
const usageTrackingStateSchema = z.object({
lastModelCallTokens: z.number().optional(),
});
const trackUsage = createMiddleware({
name: "trackUsage",
stateSchema: usageTrackingStateSchema,
wrapModelCall: async (request, handler) => {
const response = await handler(request);
return new Command({ update: { lastModelCallTokens: 150 } });
},
});Command 会流经图的 reducer,因此更新能够被正确应用,并且消息是追加累加的,而不是替换现有状态。
多个中间件的组合
当多个中间件层返回响应时,框架会传递最后生成的 AIMessage:
- AIMessage 流转: 每个中间件的
handler()都会接收来自上一层的AIMessage。当某个中间件返回AIMessage时,它会成为下一个中间件处理函数的输入。 - 不包含消息更新的 Command 视为透传: 如果中间件返回的
Command其状态更新未涉及messages,框架会将其视为消息流转中的空操作(no-op)。下一个中间件的处理函数将接收来自返回 Command 那个中间件之前的中间件所产生的AIMessage。 - Reducer 行为与重试安全性: Command 仍然会通过 reducer 生效(消息追加累加,发生冲突时外层优先)。重试逻辑会丢弃较早调用的 Command。
import * as z from "zod";
import { createMiddleware } from "langchain";
import { Command, StateSchema, ReducedValue } from "@langchain/langgraph";
import { AIMessage, SystemMessage } from "@langchain/core/messages";
/** 后赢(Last-wins)reducer:当两个中间件同时写入时,外层覆盖内层。 */
const customMiddlewareStateSchema = new StateSchema({
traceLayer: new ReducedValue(
z.string().optional(),
{ reducer: (a, b) => b },
),
});
const outerMiddleware = createMiddleware({
name: "OuterMiddleware",
stateSchema: customMiddlewareStateSchema,
wrapModelCall: async (_request, handler) => {
await handler(_request);
return new Command({
update: {
traceLayer: "outer",
messages: [new SystemMessage({ content: "[Outer ran]" })],
},
});
},
});
const innerMiddleware = createMiddleware({
name: "InnerMiddleware",
stateSchema: customMiddlewareStateSchema,
wrapModelCall: async (_request, handler) => {
await handler(_request);
return new Command({
update: {
traceLayer: "inner",
messages: [new SystemMessage({ content: "[Inner ran]" })],
},
});
},
});创建中间件
python An AgentMiddleware 子类可以声明三个类属性,Agent 工厂会在编译时读取它们:
state_schema— 扩展带有自定义字段的 Agent 状态。参见自定义状态模式。tools— 注册随中间件一起提供的额外工具(例如待办列表中间件上的write_todos)。transformers— 注册具备作用域感知的流转换器工厂。参见自定义流转换器。 :::
createMiddleware 接受三个配置字段,Agent 工厂会在编译时读取它们:
stateSchema— 扩展带有自定义字段的 Agent 状态。参见自定义状态模式。tools— 注册随中间件一起提供的额外工具。streamTransformers— 注册具备作用域感知的流转换器工厂。参见自定义流转换器。
示例:
from langchain.agents.middleware import (
AgentMiddleware,
AgentState,
ModelRequest,
ModelResponse,
)
from langgraph.runtime import Runtime
from typing import Any, Callable
class LoggingMiddleware(AgentMiddleware):
def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
print(f"About to call model with {len(state['messages'])} messages")
return None
def after_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
print(f"Model returned: {state['messages'][-1].content}")
return None
async def abefore_model(
self, state: AgentState, runtime: Runtime
) -> dict[str, Any] | None:
# before_model 的异步版本
return None
async def aafter_model(
self, state: AgentState, runtime: Runtime
) -> dict[str, Any] | None:
# after_model 的异步版本
print(f"Model returned: {state['messages'][-1].content}")
return None
agent = create_agent(
model="gpt-5.5",
middleware=[LoggingMiddleware()],
tools=[...],
)何时使用类:
- 为同一个钩子同时定义同步和异步实现
- 单个中间件中需要多个钩子
- 需要复杂配置(例如可配置的阈值、自定义模型)
- 通过初始化时配置在不同项目间复用
:::
使用 createMiddleware 函数定义自定义中间件:
import { createMiddleware } from "langchain";
const loggingMiddleware = createMiddleware({
name: "LoggingMiddleware",
beforeModel: (state) => {
console.log(`About to call model with ${state.messages.length} messages`);
return;
},
afterModel: (state) => {
const lastMessage = state.messages[state.messages.length - 1];
console.log(`Model returned: ${lastMessage.content}`);
return;
},
});自定义状态模式
如果你的中间件需要在不同钩子之间跟踪状态,中间件可以使用自定义属性扩展 Agent 的状态。这使得中间件能够:
跨执行过程跟踪状态:维护在整个 Agent 执行生命周期中持久存在的计数器、标志位或其他值
在钩子之间共享数据:将信息从
beforeModel传递给afterModel,或在不同的中间件实例之间传递实现横切关注点:在不修改核心 Agent 逻辑的情况下,添加限流、使用量统计、用户上下文或审计日志等功能
做出条件决策:使用累积的状态来决定是继续执行、跳转到不同节点,还是动态修改行为
import { createMiddleware, createAgent, HumanMessage } from "langchain";
import { StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const CustomState = new StateSchema({
modelCallCount: z.number().default(0),
userId: z.string().optional(),
});
const callCounterMiddleware = createMiddleware({
name: "CallCounterMiddleware",
stateSchema: CustomState,
beforeModel: {
canJumpTo: ["end"],
hook: (state) => {
if (state.modelCallCount > 10) {
return { jumpTo: "end" };
}
return;
},
},
afterModel: (state) => {
return { modelCallCount: state.modelCallCount + 1 };
},
});
const agent = createAgent({
model: "gpt-5.5",
tools: [...],
middleware: [callCounterMiddleware],
});
const result = await agent.invoke({
messages: [new HumanMessage("Hello")],
modelCallCount: 0,
userId: "user-123",
});状态字段可以是公开的(public),也可以是私有的(private)。以下划线(_)开头的字段被视为私有字段,不会包含在 Agent 的返回结果中。仅返回公开字段(没有前导下划线的字段)。
这对于存储不应暴露给调用方的中间件内部状态非常有用,例如临时跟踪变量或内部标志位:
import { StateSchema } from "@langchain/langgraph";
import * as z from "zod";
const PrivateState = new StateSchema({
// 公开字段 - 包含在 invoke 结果中
publicCounter: z.number().default(0),
// 私有字段 - 排除在 invoke 结果之外
_internalFlag: z.boolean().default(false),
});
const middleware = createMiddleware({
name: "ExampleMiddleware",
stateSchema: PrivateState,
afterModel: (state) => {
// 两个字段在执行期间均可访问
if (state._internalFlag) {
return { publicCounter: state.publicCounter + 1 };
}
return { _internalFlag: true };
},
});
const result = await agent.invoke({
messages: [new HumanMessage("Hello")],
publicCounter: 0
});
// result 仅包含 publicCounter,不包含 _internalFlag
console.log(result.publicCounter); // 1
console.log(result._internalFlag); // undefined自定义流转换器
NOTE
中间件注册的转换器需要 langchain@1.4.3 或更高版本。
中间件可以注册流转换器工厂,将来自实时 Agent 流的事件投射到类型化的扩展通道上。这对于显露计数器、旁路产物、部分输出或网络层脱敏非常有用,且无需与框架的内置投射耦合。
在编译时,中间件注册的工厂会与调用方直接传递给 Agent 工厂的内容进行合并。最终排序规则会将内置的 ToolCallTransformer 保留在最前面,并让调用方提供的条目排在最后。
将 streamTransformers 作为工厂元组传递给 createMiddleware。每个工厂的形式为 () => StreamTransformer<any>(零参数),且每个作用域调用一次;每次调用返回一个新的转换器以保持每个子图的相互隔离。
import { createAgent, createMiddleware } from "langchain";
const toolActivityMiddleware = createMiddleware({
name: "ToolActivityMiddleware",
streamTransformers: [toolActivityTransformer],
});
const agent = createAgent({
model: "gpt-5-nano",
tools: [...],
middleware: [toolActivityMiddleware],
});有关完整的排序规则和 PII 脱敏示例,请参阅在中间件上注册转换器。
自定义上下文
中间件可以定义自定义上下文模式(context schema)以访问单次调用级别的元数据。与状态(state)不同,上下文是只读的,并且不会在各次调用之间持久化。这使其非常适合用于:
- 用户信息:传递在执行期间不会更改的用户 ID、角色或偏好设置
- 配置覆盖:提供单次调用设置,如速率限制或特性标志(feature flags)
- 租户/工作区上下文:为多租户应用程序包含特定于组织的数据
- 请求元数据:传递中间件所需的请求 ID、API 密钥或其他元数据
使用 Zod 定义上下文模式,并通过中间件钩子中的 runtime.context 访问它。上下文模式中的必填字段将在 TypeScript 层面强制执行,确保你在调用 agent.invoke() 时必须提供它们。
import { createAgent, createMiddleware, HumanMessage } from "langchain";
import * as z from "zod";
const contextSchema = z.object({
userId: z.string(),
tenantId: z.string(),
apiKey: z.string().optional(),
});
const userContextMiddleware = createMiddleware({
name: "UserContextMiddleware",
contextSchema,
wrapModelCall: (request, handler) => {
// 从 runtime 中访问上下文
const { userId, tenantId } = request.runtime.context;
// 向系统消息中添加用户上下文
const contextText = `User ID: ${userId}, Tenant: ${tenantId}`;
const newSystemMessage = request.systemMessage.concat(contextText);
return handler({
...request,
systemMessage: newSystemMessage,
});
},
});
const agent = createAgent({
model: "gpt-5.5",
middleware: [userContextMiddleware],
tools: [],
contextSchema,
});
const result = await agent.invoke(
{ messages: [new HumanMessage("Hello")] },
// 必须提供必填字段(userId, tenantId)
{
context: {
userId: "user-123",
tenantId: "acme-corp",
},
}
);必填上下文字段:当你在 contextSchema 中定义必填字段时(没有 .optional() 或 .default() 的字段),TypeScript 将强制在 agent.invoke() 调用期间必须提供这些字段。这确保了类型安全,并避免因缺少必需上下文而导致的运行时错误。
// 如果缺少 userId 或 tenantId,这将导致 TypeScript 错误
const result = await agent.invoke(
{ messages: [new HumanMessage("Hello")] },
{ context: { userId: "user-123" } } // 错误:tenantId 是必填的
);执行顺序
当使用多个中间件时,请理解它们的执行流程:
const agent = createAgent({
model: "gpt-5.5",
middleware: [middleware1, middleware2, middleware3],
tools: [...],
});执行流程
Before 钩子按顺序运行:
middleware1.before_agent()middleware2.before_agent()middleware3.before_agent()Agent 循环开始
middleware1.before_model()middleware2.before_model()middleware3.before_model()Wrap 钩子像函数调用一样嵌套:
middleware1.wrap_model_call()→middleware2.wrap_model_call()→middleware3.wrap_model_call()→ modelAfter 钩子按逆序运行:
middleware3.after_model()middleware2.after_model()middleware1.after_model()Agent 循环结束
middleware3.after_agent()middleware2.after_agent()middleware1.after_agent()
核心规则:
before_*钩子:从前往后(正序)after_*钩子:从后往前(逆序)wrap_*钩子:嵌套(第一个中间件包裹所有其他中间件)
Agent 跳转
要从中间件中提前退出,返回一个包含 jump_to 的字典:
可用的跳转目标:
'end':跳转到 Agent 执行的末尾(或第一个after_agent钩子)'tools':跳转到 tools 节点'model':跳转到 model 节点(或第一个before_model钩子)
import { createAgent, createMiddleware, AIMessage } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
middleware: [
createMiddleware({
name: "BlockedContentMiddleware",
beforeModel: {
canJumpTo: ["end"],
hook: (state) => {
if (state.messages.at(-1)?.content.includes("BLOCKED")) {
return {
messages: [new AIMessage("I cannot respond to that request.")],
jumpTo: "end" as const,
};
}
return;
},
},
}),
],
});
const result = await agent.invoke({
messages: "Hello, world! BLOCKED"
});
/**
* Expected output:
* I cannot respond to that request.
*/
console.log(result.messages.at(-1)?.content);最佳实践
- 保持中间件职责单一 —— 每个中间件应当专精于一件事
- 优雅地处理错误 —— 不要让中间件的错误导致 Agent 崩溃
- 使用合适的钩子类型:
- 节点风格用于顺序逻辑(日志记录、校验)
- 包裹风格用于控制流(重试、降级、缓存)
- 清晰记录所有自定义状态属性
- 在集成之前对中间件进行独立的单元测试
- 考虑执行顺序 —— 将关键中间件放在列表的最前面
- 尽可能使用内置中间件
示例
动态提示词
在运行时动态修改系统提示词,以便在每次模型调用前注入上下文、特定于用户的指令或其他信息。这是最常见的中间件使用场景之一。
使用 ModelRequest 中的 systemMessage 字段来读取和修改系统提示词。它包含一个 SystemMessage 对象(即使 Agent 是使用字符串 systemPrompt 创建的)。
import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "google:gemini-3.6-flash",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "openai:gpt-5.5",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "anthropic:claude-sonnet-5",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "openrouter:z-ai/glm-5.2",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "fireworks:accounts/fireworks/models/glm-5p2",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "baseten:zai-org/GLM-5.2",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});import { createMiddleware, SystemMessage, createAgent } from "langchain";
const addContextMiddleware = createMiddleware({
name: "AddContextMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
const agent = createAgent({
model: "ollama:north-mini-code-1.0",
systemPrompt: "You are a helpful assistant.",
middleware: [addContextMiddleware],
});使用 SystemMessage.concat 可以保留由其他中间件创建的缓存控制元数据或结构化内容块。
动态模型选择
import { createMiddleware, initChatModel } from "langchain";
const models = {
complex: await initChatModel("claude-sonnet-4-6"),
simple: await initChatModel("claude-haiku-4-5-20251001"),
};
const dynamicModelMiddleware = createMiddleware({
name: "DynamicModelMiddleware",
wrapModelCall: (request, handler) => {
const modifiedRequest = { ...request };
if (request.messages.length > 10) {
modifiedRequest.model = models.complex;
} else {
modifiedRequest.model = models.simple;
}
return handler(modifiedRequest);
},
});动态选择工具
在运行时选择相关工具以提高性能和准确性。本节介绍如何过滤预先注册的工具。有关注册运行时发现的工具(例如来自 MCP 服务器),请参阅运行时工具注册。
优势:
- 更短的提示词 - 仅暴露相关工具以降低复杂度
- 更高的准确性 - 模型能更准确地从较少的选项中做出选择
- 权限控制 - 根据用户权限动态过滤工具
import { createAgent, createMiddleware } from "langchain";
const toolSelectorMiddleware = createMiddleware({
name: "ToolSelector",
wrapModelCall: (request, handler) => {
// 根据状态/上下文选择少量相关的工具子集
const relevantTools = selectRelevantTools(request.state, request.runtime);
const modifiedRequest = { ...request, tools: relevantTools };
return handler(modifiedRequest);
},
});
const agent = createAgent({
model: "gpt-5.5",
tools: allTools,
middleware: [toolSelectorMiddleware],
});工具调用监控
import { createMiddleware } from "langchain";
const toolMonitoringMiddleware = createMiddleware({
name: "ToolMonitoringMiddleware",
wrapToolCall: (request, handler) => {
console.log(`Executing tool: ${request.toolCall.name}`);
console.log(`Arguments: ${JSON.stringify(request.toolCall.args)}`);
try {
const result = handler(request);
console.log("Tool completed successfully");
return result;
} catch (e) {
console.log(`Tool failed: ${e}`);
throw e;
}
},
});提示词缓存(Anthropic)
使用 Anthropic 模型时,可以使用带有缓存控制指令的结构化内容块来缓存大型系统提示词:
装饰器
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from langchain.messages import SystemMessage
from typing import Callable
@wrap_model_call
def add_cached_context(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
# 始终处理内容块
new_content = list(request.system_message.content_blocks) + [
{
"type": "text",
"text": "Here is a large document to analyze:\n\n<document>...</document>",
# 截至此点的内容将被缓存
"cache_control": {"type": "ephemeral"}
}
]
new_system_message = SystemMessage(content=new_content)
return handler(request.override(system_message=new_system_message))类
from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
from langchain.messages import SystemMessage
from typing import Callable
class CachedContextMiddleware(AgentMiddleware):
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
# 始终处理内容块
new_content = list(request.system_message.content_blocks) + [
{
"type": "text",
"text": "Here is a large document to analyze:\n\n<document>...</document>",
"cache_control": {"type": "ephemeral"} # 此内容将被缓存
}
]
new_system_message = SystemMessage(content=new_content)
return handler(request.override(system_message=new_system_message))注意事项:
ModelRequest.system_message始终是一个SystemMessage对象,即使 Agent 是使用system_prompt="string"创建的- 使用
SystemMessage.content_blocks将内容作为块列表进行访问,无论原始内容是字符串还是列表 - 修改系统消息时,请使用
content_blocks并追加新块以保留现有结构 - 对于缓存控制等高级使用场景,你可以将
SystemMessage对象直接传递给create_agent的system_prompt参数
:::
使用 ModelRequest 中的 systemMessage 字段在中间件中修改系统消息。它包含一个 SystemMessage 对象(即使 Agent 是使用字符串 systemPrompt 创建的)。
示例:链式组合中间件 - 不同的中间件可以使用不同的方法:
import { createMiddleware, SystemMessage, createAgent } from "langchain";
// 中间件 1:使用带有简单拼接的 systemMessage
const myMiddleware = createMiddleware({
name: "MyMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(`Additional context.`),
});
},
});
// 中间件 2:使用带有结构化内容的 systemMessage(保留结构)
const myOtherMiddleware = createMiddleware({
name: "MyOtherMiddleware",
wrapModelCall: async (request, handler) => {
return handler({
...request,
systemMessage: request.systemMessage.concat(
new SystemMessage({
content: [
{
type: "text",
text: " More additional context. This will be cached.",
cache_control: { type: "ephemeral", ttl: "5m" },
},
],
})
),
});
},
});
const agent = createAgent({
model: "google:gemini-3.6-flash",
systemPrompt: "You are a helpful assistant.",
middleware: [myMiddleware, myOtherMiddleware],
});生成的系统消息将为:
new SystemMessage({
content: [
{ type: "text", text: "You are a helpful assistant." },
{ type: "text", text: "Additional context." },
{
type: "text",
text: " More additional context. This will be cached.",
cache_control: { type: "ephemeral", ttl: "5m" },
},
],
});使用 SystemMessage.concat 可以保留由其他中间件创建的缓存控制元数据或结构化内容块。
