概述
监督者模式 是一种 多智能体 架构,其中中央监督代理协调专门的工作代理。当任务需要不同类型的专门知识时,这种方法表现出色。与其构建一个能跨领域管理工具选择的单一代理,不如创建专注于特定领域的专家,并由一个理解整体工作流程的监督者来协调。 在本教程中,您将构建一个个人助理系统,通过一个实际的工作流来展示这些优势。该系统将协调两个职责截然不同的专家:- 一个 日历代理,负责处理日程安排、可用性检查和事件管理。
- 一个 电子邮件代理,负责管理通信、起草消息和发送通知。
为什么使用监督者?
多智能体架构允许您在工作者之间划分 工具,每个工作者都有自己的独立提示词或指令。考虑一个能直接访问所有日历和电子邮件 API 的代理:它必须从众多相似工具中进行选择,理解每个 API 的确切格式,并同时处理多个领域。如果性能下降,将相关工具和相关提示词分离到逻辑组中可能会有所帮助(部分是为了管理迭代改进)。概念
我们将涵盖以下概念:设置
安装
本教程需要langchain 包:
npm install langchain
yarn add langchain
pnpm add langchain
LangSmith
设置 LangSmith 以检查代理内部发生了什么。然后设置以下环境变量:export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
process.env.LANGSMITH_TRACING = "true";
process.env.LANGSMITH_API_KEY = "...";
组件
我们需要从 LangChain 的集成套件中选择一个聊天模型:- OpenAI
- Anthropic
- Azure
- Google Gemini
- Bedrock Converse
👉 阅读 OpenAI 聊天模型集成文档
npm install @langchain/openai
pnpm install @langchain/openai
yarn add @langchain/openai
bun add @langchain/openai
import { initChatModel } from "langchain";
process.env.OPENAI_API_KEY = "你的 API 密钥";
const model = await initChatModel("gpt-5.5");
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-5.5",
apiKey: "你的 API 密钥"
});
👉 阅读 Anthropic 聊天模型集成文档
npm install @langchain/anthropic
pnpm install @langchain/anthropic
yarn add @langchain/anthropic
pnpm add @langchain/anthropic
import { initChatModel } from "langchain";
process.env.ANTHROPIC_API_KEY = "你的 API 密钥";
const model = await initChatModel("claude-sonnet-4-6");
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-sonnet-4-6",
apiKey: "你的 API 密钥"
});
👉 阅读 Azure 聊天模型集成文档
npm install @langchain/azure
pnpm install @langchain/azure
yarn add @langchain/azure
bun add @langchain/azure
import { initChatModel } from "langchain";
process.env.AZURE_OPENAI_API_KEY = "你的 API 密钥";
process.env.AZURE_OPENAI_ENDPOINT = "你的端点";
process.env.OPENAI_API_VERSION = "你的 API 版本";
const model = await initChatModel("azure_openai:gpt-5.5");
import { AzureChatOpenAI } from "@langchain/openai";
const model = new AzureChatOpenAI({
model: "gpt-5.5",
azureOpenAIApiKey: "你的 API 密钥",
azureOpenAIApiEndpoint: "你的端点",
azureOpenAIApiVersion: "你的 API 版本"
});
👉 阅读 Google GenAI 聊天模型集成文档
npm install @langchain/google-genai
pnpm install @langchain/google-genai
yarn add @langchain/google-genai
bun add @langchain/google-genai
import { initChatModel } from "langchain";
process.env.GOOGLE_API_KEY = "你的 API 密钥";
const model = await initChatModel("google-genai:gemini-2.5-flash-lite");
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
const model = new ChatGoogleGenerativeAI({
model: "gemini-2.5-flash-lite",
apiKey: "你的 API 密钥"
});
👉 阅读 AWS Bedrock 聊天模型集成文档
npm install @langchain/aws
pnpm install @langchain/aws
yarn add @langchain/aws
bun add @langchain/aws
import { initChatModel } from "langchain";
// 按照此处的步骤配置您的凭据:
// https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
const model = await initChatModel("bedrock:gpt-5.5");
import { ChatBedrockConverse } from "@langchain/aws";
// 按照此处的步骤配置您的凭据:
// https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
const model = new ChatBedrockConverse({
model: "gpt-5.5",
region: "us-east-2"
});
1. 定义工具
首先定义需要结构化输入的工具。在实际应用中,这些工具会调用真实的 API(例如 Google Calendar、SendGrid 等)。在本教程中,您将使用桩代码来演示该模式。import { tool } from "langchain";
import { z } from "zod";
const createCalendarEvent = tool(
async ({ title, startTime, endTime, attendees, location }) => {
// 桩代码: 实际中,这里会调用 Google Calendar API、Outlook API 等。
return `事件已创建: ${title},时间从 ${startTime} 到 ${endTime},有 ${attendees.length} 位参与者`;
},
{
name: "create_calendar_event",
description: "创建一个日历事件。需要精确的 ISO 日期时间格式。",
schema: z.object({
title: z.string(),
startTime: z.string().describe("ISO 格式: '2024-01-15T14:00:00'"),
endTime: z.string().describe("ISO 格式: '2024-01-15T15:00:00'"),
attendees: z.array(z.string()).describe("电子邮件地址"),
location: z.string().optional(),
}),
}
);
const sendEmail = tool(
async ({ to, subject, body, cc }) => {
// 桩代码: 实际中,这里会调用 SendGrid、Gmail API 等。
return `电子邮件已发送给 ${to.join(', ')} - 主题: ${subject}`;
},
{
name: "send_email",
description: "通过电子邮件 API 发送邮件。需要格式正确的地址。",
schema: z.object({
to: z.array(z.string()).describe("电子邮件地址"),
subject: z.string(),
body: z.string(),
cc: z.array(z.string()).optional(),
}),
}
);
const getAvailableTimeSlots = tool(
async ({ attendees, date, durationMinutes }) => {
// 桩代码: 实际中,这里会查询日历 API
return ["09:00", "14:00", "16:00"];
},
{
name: "get_available_time_slots",
description: "检查特定日期给定参与者的日历可用性。",
schema: z.object({
attendees: z.array(z.string()),
date: z.string().describe("ISO 格式: '2024-01-15'"),
durationMinutes: z.number(),
}),
}
);
2. 创建专门的子代理
接下来,我们将创建处理每个领域的专门子代理。创建日历代理
日历代理理解自然语言的调度请求,并将其转换为精确的 API 调用。它处理日期解析、可用性检查和事件创建。import { createAgent } from "langchain";
const now = new Date();
const today = [
now.getFullYear(),
String(now.getMonth() + 1).padStart(2, "0"),
String(now.getDate()).padStart(2, "0"),
].join("-");
const CALENDAR_AGENT_PROMPT = `
今天的日期是 ${today}。
你是一个日历调度助手。
解析自然语言的调度请求(例如,'下周二下午2点')
为正确的 ISO 日期时间格式。
在需要时使用 get_available_time_slots 检查可用性。
如果没有合适的时间段,请停止并在回复中确认不可用。
使用 create_calendar_event 来安排事件。
在最终回复中始终确认已安排的内容。
`.trim();
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: CALENDAR_AGENT_PROMPT,
});
const query = "安排一个明天下午2点开始、时长1小时的团队会议";
const stream = await calendarAgent.streamEvents(
{ messages: [{ role: "user", content: query }] },
{ version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
console.log(`工具结果: ${await call.output}`);
}
})(),
]);
================================== Ai 消息 ==================================
工具调用:
get_available_time_slots (call_EIeoeIi1hE2VmwZSfHStGmXp)
调用 ID: call_EIeoeIi1hE2VmwZSfHStGmXp
参数:
attendees: []
date: 2024-06-18
duration_minutes: 60
================================= 工具消息 =================================
名称: get_available_time_slots
["09:00", "14:00", "16:00"]
================================== Ai 消息 ==================================
工具调用:
create_calendar_event (call_zgx3iJA66Ut0W8S3NpT93kEB)
调用 ID: call_zgx3iJA66Ut0W8S3NpT93kEB
参数:
title: Team Meeting
start_time: 2024-06-18T14:00:00
end_time: 2024-06-18T15:00:00
attendees: []
================================= 工具消息 =================================
名称: create_calendar_event
事件已创建: Team Meeting,时间从 2024-06-18T14:00:00 到 2024-06-18T15:00:00,有 0 位参与者
================================== Ai 消息 ==================================
团队会议已安排在明天,6月18日,下午2:00开始,持续1小时。如果您需要添加参与者或地点,请告诉我!
create_calendar_event,并返回自然语言确认。
创建电子邮件代理
电子邮件代理处理消息的撰写和发送。它专注于提取收件人信息、制作合适的主题行和正文,并管理电子邮件通信。const EMAIL_AGENT_PROMPT = `
你是一个电子邮件助手。
根据自然语言请求撰写专业的电子邮件。
提取收件人信息并制作合适的主题行和正文。
使用 send_email 来发送消息。
在最终回复中始终确认已发送的内容。
`.trim();
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: EMAIL_AGENT_PROMPT,
});
const query = "给设计团队发一封关于审查新原型的提醒邮件";
const stream = await emailAgent.streamEvents(
{ messages: [{ role: "user", content: query }] },
{ version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
console.log(`工具结果: ${await call.output}`);
}
})(),
]);
================================== Ai 消息 ==================================
工具调用:
send_email (call_OMl51FziTVY6CRZvzYfjYOZr)
调用 ID: call_OMl51FziTVY6CRZvzYfjYOZr
参数:
to: ['design-team@example.com']
subject: Reminder: Please Review the New Mockups
body: Hi Design Team,
This is a friendly reminder to review the new mockups at your earliest convenience. Your feedback is important to ensure that we stay on track with our project timeline.
Please let me know if you have any questions or need additional information.
Thank you!
Best regards,
================================= 工具消息 =================================
名称: send_email
电子邮件已发送给 design-team@example.com - 主题: Reminder: Please Review the New Mockups
================================== Ai 消息 ==================================
我已经给设计团队发送了提醒邮件,请求他们审查新的原型。如果您需要就这个话题进行进一步的沟通,请告诉我!
send_email,并返回确认。每个子代理都有一个狭窄的关注点,配备了特定领域的工具和提示词,使其能够出色地完成特定任务。
3. 将子代理包装为工具
现在,将每个子代理包装成监督者可以调用的工具。这是创建分层系统的关键架构步骤。监督者将看到像 “schedule_event” 这样的高级工具,而不是像 “create_calendar_event” 这样的低级工具。const scheduleEvent = tool(
async ({ request }) => {
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: request }]
});
const lastMessage = result.messages[result.messages.length - 1];
return lastMessage.text;
},
{
name: "schedule_event",
description: `
使用自然语言安排日历事件。
当用户想要创建、修改或检查日历预约时使用此工具。
处理日期/时间解析、可用性检查和事件创建。
输入:自然语言调度请求(例如,'下周二下午2点与设计团队开会')
`.trim(),
schema: z.object({
request: z.string().describe("自然语言调度请求"),
}),
}
);
const manageEmail = tool(
async ({ request }) => {
const result = await emailAgent.invoke({
messages: [{ role: "user", content: request }]
});
const lastMessage = result.messages[result.messages.length - 1];
return lastMessage.text;
},
{
name: "manage_email",
description: `
使用自然语言发送电子邮件。
当用户想要发送通知、提醒或任何电子邮件通信时使用此工具。
处理收件人提取、主题生成和邮件撰写。
输入:自然语言电子邮件请求(例如,'给他们发一封关于会议的提醒')
`.trim(),
schema: z.object({
request: z.string().describe("自然语言电子邮件请求"),
}),
}
);
4. 创建监督者代理
现在创建协调子代理的监督者。监督者只看到高级工具,并在域级别(而非单个 API 级别)做出路由决策。const SUPERVISOR_PROMPT = `
你是一个乐于助人的个人助理。
你可以安排日历事件和发送电子邮件。
将用户请求分解为适当的工具调用,并协调结果。
当一个请求涉及多个操作时,请顺序或并行使用多个工具。
`.trim();
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: SUPERVISOR_PROMPT,
});
5. 使用监督者
现在用需要跨多个领域协调的复杂请求来测试您完整的系统:示例 1:简单的单域请求
const query = "安排明天上午9点的团队站会";
const stream = await supervisorAgent.streamEvents(
{ messages: [{ role: "user", content: query }] },
{ version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
console.log(`工具结果: ${await call.output}`);
}
})(),
]);
================================== Ai 消息 ==================================
工具调用:
schedule_event (call_mXFJJDU8bKZadNUZPaag8Lct)
调用 ID: call_mXFJJDU8bKZadNUZPaag8Lct
参数:
request: 安排明天上午9点有 Alice 和 Bob 参加的团队站会。
================================= 工具消息 =================================
名称: schedule_event
明天上午9:00有 Alice 和 Bob 参加的团队站会已安排好。如果您需要进行任何更改或添加更多细节,请告诉我!
================================== Ai 消息 ==================================
明天上午9:00有 Alice 和 Bob 参加的团队站会已安排好。如果您需要进一步的安排或调整,请告诉我!
schedule_event,然后日历代理处理日期解析和事件创建。
如需全面了解信息流,包括每个聊天模型调用的提示词和响应,请查看上述运行的 LangSmith 跟踪。
示例 2:复杂的多域请求
const query =
"安排一个下周二下午2点开始、时长1小时的与设计团队的会议," +
"并且给他们发一封关于审查新原型的电子邮件提醒。";
const stream = await supervisorAgent.streamEvents(
{ messages: [{ role: "user", content: query }] },
{ version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
console.log(`工具结果: ${await call.output}`);
}
})(),
]);
================================== Ai 消息 ==================================
工具调用:
schedule_event (call_YA68mqF0koZItCFPx0kGQfZi)
调用 ID: call_YA68mqF0koZItCFPx0kGQfZi
参数:
request: 下周二下午2点开始,时长1小时的与设计团队的会议
manage_email (call_XxqcJBvVIuKuRK794ZIzlLxx)
调用 ID: call_XxqcJBvVIuKuRK794ZIzlLxx
参数:
request: 给设计团队发一封关于在我们下周二下午2点的会议前审查新原型的电子邮件提醒
================================= 工具消息 =================================
名称: schedule_event
您与设计团队的会议已安排在下周二,6月18日,下午2:00到3:00。如果您需要添加更多细节或进行任何更改,请告诉我!
================================= 工具消息 =================================
名称: manage_email
我已经给设计团队发送了一封电子邮件提醒,请求他们审查新的原型。如果您需要包含更多信息或收件人,请告诉我!
================================== Ai 消息 ==================================
您与设计团队的会议已安排在下周二,6月18日,下午2:00到3:00。
我也给设计团队发送了一封电子邮件提醒,请他们审查新的原型。
如果您想在会议中添加更多细节或在电子邮件中包含更多信息,请告诉我!
schedule_event,然后为提醒调用 manage_email。每个子代理完成其任务,监督者将两个结果综合成一个连贯的响应。
监督者默认按顺序向子代理分派任务。每个工具调用在下一个开始前完成。然而,许多大型语言模型会在单个响应中发出多个工具调用(如上面的跟踪所示,
schedule_event 和 manage_email 被一起调用),运行时会并行执行它们。您也可以配置显式的并行分派。详情请参阅 create_supervisor 参考文档。请参阅 LangSmith 跟踪,以查看上述运行的详细信息流,包括各个聊天模型的提示词和响应。
完整的可运行示例
这是一个包含所有内容的可运行脚本:Show 查看完整代码
Show 查看完整代码
/**
* 个人助理监督者示例
*
* 本示例演示了多智能体系统的工具调用模式。
* 一个监督者代理协调被包装成工具的专门子代理(日历和电子邮件)。
*/
import { tool, createAgent } from "langchain";
import { ChatAnthropic } from "@langchain/anthropic";
import { z } from "zod";
// ============================================================================
// 步骤 1: 定义底层 API 工具(桩代码)
// ============================================================================
const createCalendarEvent = tool(
async ({ title, startTime, endTime, attendees, location }) => {
// 桩代码: 实际中,这里会调用 Google Calendar API、Outlook API 等。
return `事件已创建: ${title},时间从 ${startTime} 到 ${endTime},有 ${attendees.length} 位参与者`;
},
{
name: "create_calendar_event",
description: "创建一个日历事件。需要精确的 ISO 日期时间格式。",
schema: z.object({
title: z.string(),
startTime: z.string().describe("ISO 格式: '2024-01-15T14:00:00'"),
endTime: z.string().describe("ISO 格式: '2024-01-15T15:00:00'"),
attendees: z.array(z.string()).describe("电子邮件地址"),
location: z.string().optional().default(""),
}),
}
);
const sendEmail = tool(
async ({ to, subject, body, cc }) => {
// 桩代码: 实际中,这里会调用 SendGrid、Gmail API 等。
return `电子邮件已发送给 ${to.join(", ")} - 主题: ${subject}`;
},
{
name: "send_email",
description: "通过电子邮件 API 发送邮件。需要格式正确的地址。",
schema: z.object({
to: z.array(z.string()).describe("电子邮件地址"),
subject: z.string(),
body: z.string(),
cc: z.array(z.string()).optional().default([]),
}),
}
);
const getAvailableTimeSlots = tool(
async ({ attendees, date, durationMinutes }) => {
// 桩代码: 实际中,这里会查询日历 API
return ["09:00", "14:00", "16:00"];
},
{
name: "get_available_time_slots",
description: "检查特定日期给定参与者的日历可用性。",
schema: z.object({
attendees: z.array(z.string()),
date: z.string().describe("ISO 格式: '2024-01-15'"),
durationMinutes: z.number(),
}),
}
);
// ============================================================================
// 步骤 2: 创建专门的子代理
// ============================================================================
const llm = new ChatAnthropic({
model: "gpt-5.5",
});
const now = new Date();
const today = [
now.getFullYear(),
String(now.getMonth() + 1).padStart(2, "0"),
String(now.getDate()).padStart(2, "0"),
].join("-");
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: `
今天的日期是 ${today}。
你是一个日历调度助手。
解析自然语言的调度请求(例如,'下周二下午2点')
为正确的 ISO 日期时间格式。
在需要时使用 get_available_time_slots 检查可用性。
如果没有合适的时间段,请停止并在回复中确认不可用。
使用 create_calendar_event 来安排事件。
在最终回复中始终确认已安排的内容。
`.trim(),
});
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: `
你是一个电子邮件助手。
根据自然语言请求撰写专业的电子邮件。
提取收件人信息并制作合适的主题行和正文。
使用 send_email 来发送消息。
在最终回复中始终确认已发送的内容。
`.trim(),
});
// ============================================================================
// 步骤 3: 将子代理包装成监督者的工具
// ============================================================================
const scheduleEvent = tool(
async ({ request }) => {
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: request }],
});
const lastMessage = result.messages[result.messages.length - 1];
return lastMessage.text;
},
{
name: "schedule_event",
description: `
使用自然语言安排日历事件。
当用户想要创建、修改或检查日历预约时使用此工具。
处理日期/时间解析、可用性检查和事件创建。
输入:自然语言调度请求(例如,'下周二下午2点与设计团队开会')
`.trim(),
schema: z.object({
request: z.string().describe("自然语言调度请求"),
}),
}
);
const manageEmail = tool(
async ({ request }) => {
const result = await emailAgent.invoke({
messages: [{ role: "user", content: request }],
});
const lastMessage = result.messages[result.messages.length - 1];
return lastMessage.text;
},
{
name: "manage_email",
description: `
使用自然语言发送电子邮件。
当用户想要发送通知、提醒或任何电子邮件通信时使用此工具。
处理收件人提取、主题生成和邮件撰写。
输入:自然语言电子邮件请求(例如,'给他们发一封关于会议的提醒')
`.trim(),
schema: z.object({
request: z.string().describe("自然语言电子邮件请求"),
}),
}
);
// ============================================================================
// 步骤 4: 创建监督者代理
// ============================================================================
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: `
你是一个乐于助人的个人助理。
你可以安排日历事件和发送电子邮件。
将用户请求分解为适当的工具调用,并协调结果。
当一个请求涉及多个操作时,请顺序或并行使用多个工具。
`.trim(),
});
// ============================================================================
// 步骤 5: 使用监督者
// ============================================================================
// 示例:需要日历和电子邮件协调的用户请求
const userRequest =
"安排一个下周二下午2点开始、时长1小时的与设计团队的会议," +
"并且给他们发一封关于审查新原型的电子邮件提醒。";
console.log("用户请求:", userRequest);
console.log(`\n${"=".repeat(80)}\n`);
const stream = await supervisorAgent.streamEvents(
{ messages: [{ role: "user", content: userRequest }] },
{ version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
console.log(`工具结果: ${await call.output}`);
}
})(),
]);
理解架构
您的系统有三个层次。底层包含需要精确格式的刚性 API 工具。中间层包含接受自然语言、将其转换为结构化 API 调用,并返回自然语言确认的子代理。顶层包含路由到高级功能并综合结果的监督者。 这种关注点分离带来了几个好处:每一层都有聚焦的职责,您可以在不影响现有功能的情况下添加新领域,并且可以独立地测试和迭代每一层。6. 添加人工参与审核
对敏感操作纳入 人工参与审核 是审慎的做法。LangChain 包含 内置中间件 来审核工具调用,在本例中是子代理调用的工具。 让我们为两个子代理都添加人工参与审核:- 我们将
create_calendar_event和send_email工具配置为中断,允许所有 响应类型 (approve,edit,reject) - 我们仅在顶级代理中添加一个 检查点器。这是暂停和恢复执行所必需的。
import { createAgent, humanInTheLoopMiddleware } from "langchain";
import { MemorySaver } from "@langchain/langgraph";
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: CALENDAR_AGENT_PROMPT,
middleware: [
humanInTheLoopMiddleware({
interruptOn: { create_calendar_event: true },
descriptionPrefix: "日历事件待批准",
}),
],
});
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: EMAIL_AGENT_PROMPT,
middleware: [
humanInTheLoopMiddleware({
interruptOn: { send_email: true },
descriptionPrefix: "出站电子邮件待批准",
}),
],
});
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: SUPERVISOR_PROMPT,
checkpointer: new MemorySaver(),
});
const query =
"安排一个下周二下午2点开始、时长1小时的与设计团队的会议," +
"并且给他们发一封关于审查新原型的电子邮件提醒。";
const config = { configurable: { thread_id: "6" } };
const interrupts: any[] = [];
const stream = await supervisorAgent.streamEvents(
{ messages: [{ role: "user", content: query }] },
{ ...config, version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of stream.toolCalls) {
console.log(`\n工具调用: ${call.name}(${JSON.stringify(call.input)})`);
}
})(),
]);
if (stream.interrupted) {
for (const interrupt of stream.interrupts) {
interrupts.push(interrupt);
console.log(`\n已中断: ${interrupt.interruptId}`);
}
}
================================== Ai 消息 ==================================
工具调用:
schedule_event (call_t4Wyn32ohaShpEZKuzZbl83z)
调用 ID: call_t4Wyn32ohaShpEZKuzZbl83z
参数:
request: 安排一个下周二下午2点开始、时长1小时的与设计团队的会议。
manage_email (call_JWj4vDJ5VMnvkySymhCBm4IR)
调用 ID: call_JWj4vDJ5VMnvkySymhCBm4IR
参数:
request: 给设计团队发一封关于在我们下周二下午2点的会议前审查新原型的电子邮件提醒。
已中断: 4f994c9721682a292af303ec1a46abb7
已中断: 2b56f299be313ad8bc689eff02973f16
for (const interrupt of interrupts) {
for (const request of interrupt.payload.actionRequests) {
console.log(`已中断: ${interrupt.interruptId}`);
console.log(`${request.description}\n`);
}
}
已中断: 4f994c9721682a292af303ec1a46abb7
日历事件待批准
工具: create_calendar_event
参数: {'title': 'Meeting with the Design Team', 'start_time': '2024-06-18T14:00:00', 'end_time': '2024-06-18T15:00:00', 'attendees': ['design team']}
已中断: 2b56f299be313ad8bc689eff02973f16
出站电子邮件待批准
工具: send_email
参数: {'to': ['designteam@example.com'], 'subject': 'Reminder: Review New Mockups Before Meeting Next Tuesday at 2pm', 'body': "Hello Team,\n\nThis is a reminder to review the new mockups ahead of our meeting scheduled for next Tuesday at 2pm. Your feedback and insights will be valuable for our discussion and next steps.\n\nPlease ensure you've gone through the designs and are ready to share your thoughts during the meeting.\n\nThank you!\n\nBest regards,\n[Your Name]"}
Command 来为每个中断指定决策。更多详情请参阅 人工参与审核指南。为了演示目的,这里我们将接受日历事件,但编辑出站电子邮件的主题:
import { Command } from "@langchain/langgraph";
const resume: Record<string, any> = {};
for (const interrupt of interrupts) {
const actionRequest = interrupt.payload.actionRequests[0];
if (actionRequest.name === "send_email") {
// 编辑邮件
const editedAction = { ...actionRequest };
editedAction.args.subject = "Mockups reminder";
resume[interrupt.interruptId] = {
decisions: [{ type: "edit", editedAction }]
};
} else {
resume[interrupt.interruptId] = { decisions: [{ type: "approve" }] };
}
}
const resumeStream = await supervisorAgent.streamEvents(
new Command({ resume }),
{ ...config, version: "v3" }
);
await Promise.all([
(async () => {
for await (const message of resumeStream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
})(),
(async () => {
for await (const call of resumeStream.toolCalls) {
console.log(`\nTool call: ${call.name}(${JSON.stringify(call.input)})`);
}
})(),
]);
7. 进阶:控制信息流
默认情况下,子代理仅接收来自监督器的请求字符串。您可能希望传递额外的上下文,例如对话历史或用户偏好。向子代理传递额外的对话上下文
import { getCurrentTaskInput } from "@langchain/langgraph";
import { type BuiltInState, HumanMessage } from "langchain";
const scheduleEvent = tool(
async ({ request }, config) => {
// 自定义子代理接收的上下文
// 从 config 中访问完整的线程消息
const currentMessages = getCurrentTaskInput<BuiltInState>(config).messages;
const originalUserMessage = currentMessages.find(HumanMessage.isInstance);
const prompt = `
You are assisting with the following user inquiry:
${originalUserMessage?.content || "No context available"}
You are tasked with the following sub-request:
${request}
`.trim();
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: prompt }],
});
const lastMessage = result.messages[result.messages.length - 1];
return lastMessage.text;
},
{
name: "schedule_event",
description: "Schedule calendar events using natural language.",
schema: z.object({
request: z.string().describe("Natural language scheduling request"),
}),
}
);
您可以在 LangSmith 追踪的 聊天模型调用 中看到子代理接收的完整上下文。
控制监督器接收的内容
您还可以自定义流回监督器的信息:const scheduleEvent = tool(
async ({ request }) => {
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: request }]
});
const lastMessage = result.messages[result.messages.length - 1];
// 选项 1:仅返回确认消息
return lastMessage.text;
// 选项 2:返回结构化数据
// return JSON.stringify({
// status: "success",
// event_id: "evt_123",
// summary: lastMessage.text
// });
},
{
name: "schedule_event",
description: "Schedule calendar events using natural language.",
schema: z.object({
request: z.string().describe("Natural language scheduling request"),
}),
}
);
要查看一个完整的可运行示例,该示例演示了具有人工审核和高级信息流控制的完整监督器模式,请查看 LangChain.js 示例中的
supervisor_complete.ts。8. 关键要点
监督器模式创建了抽象层,其中每一层都有明确的职责。在设计监督器系统时,从明确的领域边界开始,并为每个子代理提供专注的工具和提示。为监督器编写清晰的工具描述,在集成之前独立测试每一层,并根据您的具体需求控制信息流。何时使用监督器模式当您拥有多个不同的领域(日历、电子邮件、CRM、数据库),每个领域有多个工具或复杂的逻辑,您希望集中工作流控制,并且子代理不需要直接与用户对话时,请使用监督器模式。对于只有少数几个工具的简单情况,请使用单个代理。当代理需要与用户对话时,请使用 交接 代替。对于代理之间的点对点协作,请考虑其他多代理模式。
后续步骤
了解用于代理间对话的 交接,探索 上下文工程 以微调信息流,阅读 多代理概述 以比较不同模式,并使用 LangSmith 来调试和监控您的多代理系统。Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

