개요
슈퍼바이저 패턴은 중앙 슈퍼바이저 에이전트가 전문화된 워커 에이전트들을 조율하는 멀티 에이전트 아키텍처입니다. 이 접근 방식은 서로 다른 유형의 전문성이 필요한 작업에서 뛰어난 성능을 발휘합니다. 도메인 전반에 걸쳐 도구 선택을 관리하는 단일 에이전트를 구축하는 대신, 전체 워크플로를 이해하는 슈퍼바이저가 조율하는 집중된 전문가들을 만드는 것입니다. 이 튜토리얼에서는 실제 워크플로를 통해 이러한 장점을 보여주는 개인 비서 시스템을 구축합니다. 이 시스템은 근본적으로 다른 책임을 가진 두 전문가를 조율합니다:- 일정 조정, 가용성 확인, 이벤트 관리를 담당하는 캘린더 에이전트
- 커뮤니케이션 관리, 메시지 작성, 알림 전송을 담당하는 이메일 에이전트
슈퍼바이저를 사용하는 이유는?
멀티 에이전트 아키텍처를 사용하면 도구를 워커들에게 분산할 수 있으며, 각 워커는 자체 프롬프트나 지침을 가질 수 있습니다. 모든 캘린더 및 이메일 API에 직접 액세스할 수 있는 에이전트를 생각해보세요. 이 에이전트는 많은 유사한 도구 중에서 선택하고, 각 API의 정확한 형식을 이해하며, 여러 도메인을 동시에 처리해야 합니다. 성능이 저하되면 관련 도구와 관련 프롬프트를 논리적 그룹으로 분리하는 것이 도움이 될 수 있습니다(부분적으로 반복적인 개선을 관리하기 위해).개념
다음 개념을 다룹니다:설정
설치
이 튜토리얼에는langchain 패키지가 필요합니다:
Copy
npm install langchain
LangSmith
에이전트 내부에서 무슨 일이 일어나는지 검사하려면 LangSmith를 설정하세요. 그런 다음 다음 환경 변수를 설정합니다:Copy
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
컴포넌트
LangChain의 통합 제품군에서 채팅 모델을 선택해야 합니다:- OpenAI
- Anthropic
- Azure
- Google Gemini
- Bedrock Converse
👉 Read the OpenAI chat model integration docs
Copy
npm install @langchain/openai
Copy
import { initChatModel } from "langchain";
process.env.OPENAI_API_KEY = "your-api-key";
const model = await initChatModel("openai:gpt-4.1");
👉 Read the Anthropic chat model integration docs
Copy
npm install @langchain/anthropic
Copy
import { initChatModel } from "langchain";
process.env.ANTHROPIC_API_KEY = "your-api-key";
const model = await initChatModel("anthropic:claude-sonnet-4-5");
👉 Read the Azure chat model integration docs
Copy
npm install @langchain/azure
Copy
import { initChatModel } from "langchain";
process.env.AZURE_OPENAI_API_KEY = "your-api-key";
process.env.AZURE_OPENAI_ENDPOINT = "your-endpoint";
process.env.OPENAI_API_VERSION = "your-api-version";
const model = await initChatModel("azure_openai:gpt-4.1");
👉 Read the Google GenAI chat model integration docs
Copy
npm install @langchain/google-genai
Copy
import { initChatModel } from "langchain";
process.env.GOOGLE_API_KEY = "your-api-key";
const model = await initChatModel("google_genai:gemini-2.5-flash-lite");
👉 Read the AWS Bedrock chat model integration docs
Copy
npm install @langchain/aws
Copy
import { initChatModel } from "langchain";
// Follow the steps here to configure your credentials:
// https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html
const model = await initChatModel("bedrock:gpt-4.1");
1. 도구 정의하기
구조화된 입력이 필요한 도구를 정의하는 것부터 시작하세요. 실제 애플리케이션에서는 실제 API(Google Calendar, SendGrid 등)를 호출합니다. 이 튜토리얼에서는 패턴을 보여주기 위해 스텁을 사용합니다.Copy
import { tool } from "langchain";
import { z } from "zod";
const createCalendarEvent = tool(
async ({ title, startTime, endTime, attendees, location }) => {
// Stub: In practice, this would call Google Calendar API, Outlook API, etc.
return `Event created: ${title} from ${startTime} to ${endTime} with ${attendees.length} attendees`;
},
{
name: "create_calendar_event",
description: "Create a calendar event. Requires exact ISO datetime format.",
schema: z.object({
title: z.string(),
startTime: z.string().describe("ISO format: '2024-01-15T14:00:00'"),
endTime: z.string().describe("ISO format: '2024-01-15T15:00:00'"),
attendees: z.array(z.string()).describe("email addresses"),
location: z.string().optional(),
}),
}
);
const sendEmail = tool(
async ({ to, subject, body, cc }) => {
// Stub: In practice, this would call SendGrid, Gmail API, etc.
return `Email sent to ${to.join(', ')} - Subject: ${subject}`;
},
{
name: "send_email",
description: "Send an email via email API. Requires properly formatted addresses.",
schema: z.object({
to: z.array(z.string()).describe("email addresses"),
subject: z.string(),
body: z.string(),
cc: z.array(z.string()).optional(),
}),
}
);
const getAvailableTimeSlots = tool(
async ({ attendees, date, durationMinutes }) => {
// Stub: In practice, this would query calendar APIs
return ["09:00", "14:00", "16:00"];
},
{
name: "get_available_time_slots",
description: "Check calendar availability for given attendees on a specific date.",
schema: z.object({
attendees: z.array(z.string()),
date: z.string().describe("ISO format: '2024-01-15'"),
durationMinutes: z.number(),
}),
}
);
2. 전문화된 서브 에이전트 생성하기
다음으로, 각 도메인을 처리하는 전문화된 서브 에이전트를 생성합니다.캘린더 에이전트 생성하기
캘린더 에이전트는 자연어 일정 요청을 이해하고 이를 정확한 API 호출로 변환합니다. 날짜 파싱, 가용성 확인, 이벤트 생성을 처리합니다.Copy
import { createAgent } from "langchain";
const CALENDAR_AGENT_PROMPT = `
You are a calendar scheduling assistant.
Parse natural language scheduling requests (e.g., 'next Tuesday at 2pm')
into proper ISO datetime formats.
Use get_available_time_slots to check availability when needed.
Use create_calendar_event to schedule events.
Always confirm what was scheduled in your final response.
`.trim();
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: CALENDAR_AGENT_PROMPT,
});
Copy
const query = "Schedule a team meeting next Tuesday at 2pm for 1 hour";
const stream = await calendarAgent.stream({
messages: [{ role: "user", content: query }]
});
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
Copy
================================== Ai Message ==================================
Tool Calls:
get_available_time_slots (call_EIeoeIi1hE2VmwZSfHStGmXp)
Call ID: call_EIeoeIi1hE2VmwZSfHStGmXp
Args:
attendees: []
date: 2024-06-18
duration_minutes: 60
================================= Tool Message =================================
Name: get_available_time_slots
["09:00", "14:00", "16:00"]
================================== Ai Message ==================================
Tool Calls:
create_calendar_event (call_zgx3iJA66Ut0W8S3NpT93kEB)
Call ID: call_zgx3iJA66Ut0W8S3NpT93kEB
Args:
title: Team Meeting
start_time: 2024-06-18T14:00:00
end_time: 2024-06-18T15:00:00
attendees: []
================================= Tool Message =================================
Name: create_calendar_event
Event created: Team Meeting from 2024-06-18T14:00:00 to 2024-06-18T15:00:00 with 0 attendees
================================== Ai Message ==================================
The team meeting has been scheduled for next Tuesday, June 18th, at 2:00 PM and will last for 1 hour. If you need to add attendees or a location, please let me know!
create_calendar_event를 호출하고, 자연어 확인 메시지를 반환합니다.
이메일 에이전트 생성하기
이메일 에이전트는 메시지 작성 및 전송을 처리합니다. 수신자 정보 추출, 적절한 제목 및 본문 텍스트 작성, 이메일 커뮤니케이션 관리에 집중합니다.Copy
const EMAIL_AGENT_PROMPT = `
You are an email assistant.
Compose professional emails based on natural language requests.
Extract recipient information and craft appropriate subject lines and body text.
Use send_email to send the message.
Always confirm what was sent in your final response.
`.trim();
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: EMAIL_AGENT_PROMPT,
});
Copy
const query = "Send the design team a reminder about reviewing the new mockups";
const stream = await emailAgent.stream({
messages: [{ role: "user", content: query }]
});
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
Copy
================================== Ai Message ==================================
Tool Calls:
send_email (call_OMl51FziTVY6CRZvzYfjYOZr)
Call ID: call_OMl51FziTVY6CRZvzYfjYOZr
Args:
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,
================================= Tool Message =================================
Name: send_email
Email sent to design-team@example.com - Subject: Reminder: Please Review the New Mockups
================================== Ai Message ==================================
I've sent a reminder to the design team asking them to review the new mockups. If you need any further communication on this topic, just let me know!
send_email을 호출하고, 확인 메시지를 반환합니다. 각 서브 에이전트는 도메인별 도구와 프롬프트를 사용하여 좁은 범위에 집중하므로 특정 작업에서 뛰어난 성능을 발휘할 수 있습니다.
3. 서브 에이전트를 도구로 래핑하기
이제 각 서브 에이전트를 슈퍼바이저가 호출할 수 있는 도구로 래핑합니다. 이것이 계층화된 시스템을 생성하는 핵심 아키텍처 단계입니다. 슈퍼바이저는 “create_calendar_event”와 같은 저수준 도구가 아니라 “schedule_event”와 같은 고수준 도구를 보게 됩니다.Copy
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: `
Schedule calendar events using natural language.
Use this when the user wants to create, modify, or check calendar appointments.
Handles date/time parsing, availability checking, and event creation.
Input: Natural language scheduling request (e.g., 'meeting with design team next Tuesday at 2pm')
`.trim(),
schema: z.object({
request: z.string().describe("Natural language scheduling request"),
}),
}
);
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: `
Send emails using natural language.
Use this when the user wants to send notifications, reminders, or any email communication.
Handles recipient extraction, subject generation, and email composition.
Input: Natural language email request (e.g., 'send them a reminder about the meeting')
`.trim(),
schema: z.object({
request: z.string().describe("Natural language email request"),
}),
}
);
4. 슈퍼바이저 에이전트 생성하기
이제 서브 에이전트를 조율하는 슈퍼바이저를 생성합니다. 슈퍼바이저는 고수준 도구만 보고 개별 API 수준이 아닌 도메인 수준에서 라우팅 결정을 내립니다.Copy
const SUPERVISOR_PROMPT = `
You are a helpful personal assistant.
You can schedule calendar events and send emails.
Break down user requests into appropriate tool calls and coordinate the results.
When a request involves multiple actions, use multiple tools in sequence.
`.trim();
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: SUPERVISOR_PROMPT,
});
5. 슈퍼바이저 사용하기
이제 여러 도메인에 걸친 조율이 필요한 복잡한 요청으로 전체 시스템을 테스트하세요:예제 1: 단순한 단일 도메인 요청
Copy
const query = "Schedule a team standup for tomorrow at 9am";
const stream = await supervisorAgent.stream({
messages: [{ role: "user", content: query }]
});
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
Copy
================================== Ai Message ==================================
Tool Calls:
schedule_event (call_mXFJJDU8bKZadNUZPaag8Lct)
Call ID: call_mXFJJDU8bKZadNUZPaag8Lct
Args:
request: Schedule a team standup for tomorrow at 9am with Alice and Bob.
================================= Tool Message =================================
Name: schedule_event
The team standup has been scheduled for tomorrow at 9:00 AM with Alice and Bob. If you need to make any changes or add more details, just let me know!
================================== Ai Message ==================================
The team standup with Alice and Bob is scheduled for tomorrow at 9:00 AM. If you need any further arrangements or adjustments, please let me know!
schedule_event를 호출하며, 캘린더 에이전트가 날짜 파싱과 이벤트 생성을 처리합니다.
각 채팅 모델 호출에 대한 프롬프트와 응답을 포함한 정보 흐름의 완전한 투명성을 확인하려면 위 실행에 대한 LangSmith 트레이스를 확인하세요.
예제 2: 복잡한 다중 도메인 요청
Copy
const query =
"Schedule a meeting with the design team next Tuesday at 2pm for 1 hour, " +
"and send them an email reminder about reviewing the new mockups.";
const stream = await supervisorAgent.stream({
messages: [{ role: "user", content: query }]
});
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
Copy
================================== Ai Message ==================================
Tool Calls:
schedule_event (call_YA68mqF0koZItCFPx0kGQfZi)
Call ID: call_YA68mqF0koZItCFPx0kGQfZi
Args:
request: meeting with the design team next Tuesday at 2pm for 1 hour
manage_email (call_XxqcJBvVIuKuRK794ZIzlLxx)
Call ID: call_XxqcJBvVIuKuRK794ZIzlLxx
Args:
request: send the design team an email reminder about reviewing the new mockups
================================= Tool Message =================================
Name: schedule_event
Your meeting with the design team is scheduled for next Tuesday, June 18th, from 2:00pm to 3:00pm. Let me know if you need to add more details or make any changes!
================================= Tool Message =================================
Name: manage_email
I've sent an email reminder to the design team requesting them to review the new mockups. If you need to include more information or recipients, just let me know!
================================== Ai Message ==================================
Your meeting with the design team is scheduled for next Tuesday, June 18th, from 2:00pm to 3:00pm.
I've also sent an email reminder to the design team, asking them to review the new mockups.
Let me know if you'd like to add more details to the meeting or include additional information in the email!
schedule_event를 호출한 다음 리마인더를 위해 manage_email을 호출합니다. 각 서브 에이전트가 작업을 완료하면, 슈퍼바이저는 두 결과를 일관된 응답으로 종합합니다.
개별 채팅 모델 프롬프트와 응답을 포함하여 위 실행에 대한 자세한 정보 흐름을 보려면 LangSmith 트레이스를 참조하세요.
완전한 작동 예제
실행 가능한 스크립트로 모든 것을 통합한 예제입니다:Show 전체 코드 보기
Show 전체 코드 보기
Copy
/**
* Personal Assistant Supervisor Example
*
* This example demonstrates the tool calling pattern for multi-agent systems.
* A supervisor agent coordinates specialized sub-agents (calendar and email)
* that are wrapped as tools.
*/
import { tool, createAgent } from "langchain";
import { ChatAnthropic } from "@langchain/anthropic";
import { z } from "zod";
// ============================================================================
// Step 1: Define low-level API tools (stubbed)
// ============================================================================
const createCalendarEvent = tool(
async ({ title, startTime, endTime, attendees, location }) => {
// Stub: In practice, this would call Google Calendar API, Outlook API, etc.
return `Event created: ${title} from ${startTime} to ${endTime} with ${attendees.length} attendees`;
},
{
name: "create_calendar_event",
description: "Create a calendar event. Requires exact ISO datetime format.",
schema: z.object({
title: z.string(),
startTime: z.string().describe("ISO format: '2024-01-15T14:00:00'"),
endTime: z.string().describe("ISO format: '2024-01-15T15:00:00'"),
attendees: z.array(z.string()).describe("email addresses"),
location: z.string().optional().default(""),
}),
}
);
const sendEmail = tool(
async ({ to, subject, body, cc }) => {
// Stub: In practice, this would call SendGrid, Gmail API, etc.
return `Email sent to ${to.join(", ")} - Subject: ${subject}`;
},
{
name: "send_email",
description:
"Send an email via email API. Requires properly formatted addresses.",
schema: z.object({
to: z.array(z.string()).describe("email addresses"),
subject: z.string(),
body: z.string(),
cc: z.array(z.string()).optional().default([]),
}),
}
);
const getAvailableTimeSlots = tool(
async ({ attendees, date, durationMinutes }) => {
// Stub: In practice, this would query calendar APIs
return ["09:00", "14:00", "16:00"];
},
{
name: "get_available_time_slots",
description:
"Check calendar availability for given attendees on a specific date.",
schema: z.object({
attendees: z.array(z.string()),
date: z.string().describe("ISO format: '2024-01-15'"),
durationMinutes: z.number(),
}),
}
);
// ============================================================================
// Step 2: Create specialized sub-agents
// ============================================================================
const llm = new ChatAnthropic({
model: "claude-3-5-haiku-latest",
});
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: `
You are a calendar scheduling assistant.
Parse natural language scheduling requests (e.g., 'next Tuesday at 2pm')
into proper ISO datetime formats.
Use get_available_time_slots to check availability when needed.
Use create_calendar_event to schedule events.
Always confirm what was scheduled in your final response.
`.trim(),
});
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: `
You are an email assistant.
Compose professional emails based on natural language requests.
Extract recipient information and craft appropriate subject lines and body text.
Use send_email to send the message.
Always confirm what was sent in your final response.
`.trim(),
});
// ============================================================================
// Step 3: Wrap sub-agents as tools for the supervisor
// ============================================================================
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: `
Schedule calendar events using natural language.
Use this when the user wants to create, modify, or check calendar appointments.
Handles date/time parsing, availability checking, and event creation.
Input: Natural language scheduling request (e.g., 'meeting with design team next Tuesday at 2pm')
`.trim(),
schema: z.object({
request: z.string().describe("Natural language scheduling request"),
}),
}
);
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: `
Send emails using natural language.
Use this when the user wants to send notifications, reminders, or any email communication.
Handles recipient extraction, subject generation, and email composition.
Input: Natural language email request (e.g., 'send them a reminder about the meeting')
`.trim(),
schema: z.object({
request: z.string().describe("Natural language email request"),
}),
}
);
// ============================================================================
// Step 4: Create the supervisor agent
// ============================================================================
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: `
You are a helpful personal assistant.
You can schedule calendar events and send emails.
Break down user requests into appropriate tool calls and coordinate the results.
When a request involves multiple actions, use multiple tools in sequence.
`.trim(),
});
// ============================================================================
// Step 5: Use the supervisor
// ============================================================================
// Example: User request requiring both calendar and email coordination
const userRequest =
"Schedule a meeting with the design team next Tuesday at 2pm for 1 hour, " +
"and send them an email reminder about reviewing the new mockups.";
console.log("User Request:", userRequest);
console.log(`\n${"=".repeat(80)}\n`);
const stream = await supervisorAgent.stream({
messages: [{ role: "user", content: userRequest }],
});
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
아키텍처 이해하기
시스템은 세 개의 레이어로 구성됩니다. 최하위 레이어에는 정확한 형식이 필요한 엄격한 API 도구가 포함됩니다. 중간 레이어에는 자연어를 받아들이고, 이를 구조화된 API 호출로 변환하며, 자연어 확인을 반환하는 서브 에이전트가 포함됩니다. 최상위 레이어에는 고수준 기능으로 라우팅하고 결과를 종합하는 슈퍼바이저가 포함됩니다. 이러한 관심사의 분리는 여러 가지 이점을 제공합니다: 각 레이어는 집중된 책임을 가지며, 기존 도메인에 영향을 주지 않고 새로운 도메인을 추가할 수 있으며, 각 레이어를 독립적으로 테스트하고 반복할 수 있습니다.6. 휴먼 인 더 루프 리뷰 추가하기
민감한 작업에 대한 휴먼 인 더 루프 리뷰를 통합하는 것이 신중할 수 있습니다. LangChain에는 도구 호출을 검토하기 위한 내장 미들웨어가 포함되어 있으며, 이 경우 서브 에이전트가 호출하는 도구를 검토합니다. 두 서브 에이전트 모두에 휴먼 인 더 루프 리뷰를 추가해봅시다:create_calendar_event및send_email도구를 중단하도록 구성하여 모든 응답 유형(approve,edit,reject)을 허용합니다- 최상위 에이전트에만 체크포인터를 추가합니다. 이것은 실행을 일시 중지하고 재개하는 데 필요합니다.
Copy
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: "Calendar event pending approval",
}),
],
});
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: EMAIL_AGENT_PROMPT,
middleware: [
humanInTheLoopMiddleware({
interruptOn: { send_email: true },
descriptionPrefix: "Outbound email pending approval",
}),
],
});
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: SUPERVISOR_PROMPT,
checkpointer: new MemorySaver(),
});
Copy
const query =
"Schedule a meeting with the design team next Tuesday at 2pm for 1 hour, " +
"and send them an email reminder about reviewing the new mockups.";
const config = { configurable: { thread_id: "6" } };
const interrupts: any[] = [];
const stream = await supervisorAgent.stream(
{ messages: [{ role: "user", content: query }] },
config
);
for await (const step of stream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
} else if (Array.isArray(update)) {
const interrupt = update[0];
interrupts.push(interrupt);
console.log(`\nINTERRUPTED: ${interrupt.id}`);
}
}
}
Copy
================================== Ai Message ==================================
Tool Calls:
schedule_event (call_t4Wyn32ohaShpEZKuzZbl83z)
Call ID: call_t4Wyn32ohaShpEZKuzZbl83z
Args:
request: Schedule a meeting with the design team next Tuesday at 2pm for 1 hour.
manage_email (call_JWj4vDJ5VMnvkySymhCBm4IR)
Call ID: call_JWj4vDJ5VMnvkySymhCBm4IR
Args:
request: Send an email reminder to the design team about reviewing the new mockups before our meeting next Tuesday at 2pm.
INTERRUPTED: 4f994c9721682a292af303ec1a46abb7
INTERRUPTED: 2b56f299be313ad8bc689eff02973f16
Copy
for (const interrupt of interrupts) {
for (const request of interrupt.value.actionRequests) {
console.log(`INTERRUPTED: ${interrupt.id}`);
console.log(`${request.description}\n`);
}
}
Copy
INTERRUPTED: 4f994c9721682a292af303ec1a46abb7
Calendar event pending approval
Tool: create_calendar_event
Args: {'title': 'Meeting with the Design Team', 'start_time': '2024-06-18T14:00:00', 'end_time': '2024-06-18T15:00:00', 'attendees': ['design team']}
INTERRUPTED: 2b56f299be313ad8bc689eff02973f16
Outbound email pending approval
Tool: send_email
Args: {'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를 사용하여 ID를 참조하여 각 중단에 대한 결정을 지정할 수 있습니다. 추가 세부 정보는 휴먼 인 더 루프 가이드를 참조하세요. 데모를 위해 여기서는 캘린더 이벤트를 승인하지만 발신 이메일의 제목을 편집합니다:
Copy
import { Command } from "@langchain/langgraph";
const resume: Record<string, any> = {};
for (const interrupt of interrupts) {
const actionRequest = interrupt.value.actionRequests[0];
if (actionRequest.name === "send_email") {
// Edit email
const editedAction = { ...actionRequest };
editedAction.arguments.subject = "Mockups reminder";
resume[interrupt.id] = {
decisions: [{ type: "edit", editedAction }]
};
} else {
resume[interrupt.id] = { decisions: [{ type: "approve" }] };
}
}
const resumeStream = await supervisorAgent.stream(
new Command({ resume }),
config
);
for await (const step of resumeStream) {
for (const update of Object.values(step)) {
if (update && typeof update === "object" && "messages" in update) {
for (const message of update.messages) {
console.log(message.toFormattedString());
}
}
}
}
Copy
================================= Tool Message =================================
Name: schedule_event
Your meeting with the design team has been scheduled for next Tuesday, June 18th, from 2:00 pm to 3:00 pm.
================================= Tool Message =================================
Name: manage_email
Your email reminder to the design team has been sent. Here's what was sent:
- Recipient: designteam@example.com
- Subject: Mockups reminder
- Body: A reminder to review the new mockups before the meeting next Tuesday at 2pm, with a request for feedback and readiness for discussion.
Let me know if you need any further assistance!
================================== Ai Message ==================================
- Your meeting with the design team has been scheduled for next Tuesday, June 18th, from 2:00 pm to 3:00 pm.
- An email reminder has been sent to the design team about reviewing the new mockups before the meeting.
Let me know if you need any further assistance!
7. 고급: 정보 흐름 제어하기
기본적으로 서브 에이전트는 슈퍼바이저로부터 요청 문자열만 받습니다. 대화 기록이나 사용자 선호도와 같은 추가 컨텍스트를 전달하고 싶을 수 있습니다.서브 에이전트에 추가 대화 컨텍스트 전달하기
Copy
import { getCurrentTaskInput } from "@langchain/langgraph";
import type { InternalAgentState } from "langchain";
import { HumanMessage } from "@langchain/core/messages";
const scheduleEvent = tool(
async ({ request }, config) => {
// Customize context received by sub-agent
// Access full thread messages from the config
const currentMessages = getCurrentTaskInput<InternalAgentState>(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 트레이스의 채팅 모델 호출에서 서브 에이전트가 받는 전체 컨텍스트를 볼 수 있습니다.
슈퍼바이저가 받는 내용 제어하기
슈퍼바이저로 되돌아가는 정보를 사용자 정의할 수도 있습니다:Copy
const scheduleEvent = tool(
async ({ request }) => {
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: request }]
});
const lastMessage = result.messages[result.messages.length - 1];
// Option 1: Return just the confirmation message
return lastMessage.text;
// Option 2: Return structured data
// 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 programmatically to Claude, VSCode, and more via MCP for real-time answers.