课程目标
掌握多 Agent 系统的设计模式:supervisor(主管)、swarm(群体)、hierarchical(层级)。精读子 Agent 封装为工具的实现、withAgentName() 的消息标识机制,以及 Runtime 上下文的传递。
27.1 为什么需要多 Agent
单个 Agent 面对复杂任务时会遇到瓶颈:
- 工具过多:LLM 在 20+ 工具中选择准确率下降
- 职责混杂:一个 system prompt 难以同时描述多种角色
- 上下文膨胀:所有子任务共享同一对话历史
多 Agent 系统通过分工协作解决这些问题。
27.2 多 Agent 模式分类
| 模式 | 核心思想 | 适用场景 |
|---|---|---|
| Supervisor | 一个主管 Agent 协调多个专家子 Agent | 任务可清晰分派的场景 |
| Swarm | Agent 之间动态切换控制权 | 多角色对话 |
| Hierarchical | 多层级 supervisor | 大规模复杂系统 |
| Debate | 多个 Agent 辩论后汇总结论 | 需要多角度分析的决策 |
27.3 Supervisor 模式:子 Agent 封装为工具
LangChain.js 中多 Agent 的主要实现方式是将子 Agent 封装为工具。
源码位置: examples/src/multi-agent/subagents-personal-assistant.ts
import { tool, createAgent } from "langchain";
import { z } from "zod";
// 步骤 1:创建专家子 Agent
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent, getAvailableTimeSlots],
systemPrompt: "你是日历调度助手...",
});
const emailAgent = createAgent({
model: llm,
tools: [sendEmail],
systemPrompt: "你是邮件助手...",
});
// 步骤 2:将子 Agent 封装为工具
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: "使用自然语言调度日历事件",
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: "使用自然语言发送邮件",
schema: z.object({
request: z.string().describe("自然语言邮件请求"),
}),
}
);
// 步骤 3:创建 supervisor Agent
const supervisorAgent = createAgent({
model: llm,
tools: [scheduleEvent, manageEmail],
systemPrompt: "你是一个个人助手,可以安排日历和发送邮件...",
});
关键设计:supervisor Agent 不直接访问底层工具(如 createCalendarEvent),而是通过封装的工具接口与子 Agent 通信。这保持了职责分离。
27.4 withAgentName — Agent 身份标识
在多 Agent 系统中,不同 Agent 的消息会混在同一个对话历史中。withAgentName() 通过 XML 标签在消息内容中嵌入 Agent 名称。
源码位置: libs/langchain/src/agents/withAgentName.ts
export function withAgentName(
model: LanguageModelLike,
agentNameMode: AgentNameMode
): LanguageModelLike {
let processInputMessage: (message: BaseMessageLike) => BaseMessageLike;
let processOutputMessage: (message: BaseMessage) => BaseMessage;
if (agentNameMode === "inline") {
processInputMessage = _addInlineAgentName;
processOutputMessage = _removeInlineAgentName;
}
return RunnableSequence.from([
RunnableLambda.from(processInputMessages),
model,
RunnableLambda.from(processOutputMessage),
]);
}
它构建了一个三步管道:处理输入消息 → 调用模型 → 处理输出消息。
名称编码方式
源码位置: libs/langchain/src/agents/utils.ts:97
export function _addInlineAgentName<T extends BaseMessageLike>(message: T): T | AIMessage {
if (!AIMessage.isInstance(message) || !message.name) return message;
const { name } = message;
if (typeof message.content === "string") {
return new AIMessage({
...message.lc_kwargs,
content: `<name>${name}</name><content>${message.content}</content>`,
name: undefined,
});
}
// 数组内容的处理...
}
编码前: AIMessage { name: "calendar_agent", content: "会议已安排" }
编码后: AIMessage { content: "<name>calendar_agent</name><content>会议已安排</content>" }
对应的解码函数 _removeInlineAgentName 使用正则提取内容:
const NAME_PATTERN = /<name>(.*?)<\/name>/s;
const CONTENT_PATTERN = /<content>(.*?)<\/content>/s;
在 createAgent 中使用
const agent = createAgent({
model: llm,
tools: [...],
name: "CalendarAgent", // 设置 Agent 名称
includeAgentName: "inline", // 启用 inline 名称模式
});
27.5 Runtime — Agent 运行时上下文
Runtime 类型定义了中间件可访问的运行时信息:
源码位置: libs/langchain/src/agents/runtime.ts:63
export type Runtime<TContext = unknown> = Partial<
Omit<LangGraphRuntime<TContext>, "context" | "configurable">
> & WithMaybeContext<TContext> & {
configurable?: {
thread_id?: string;
[key: string]: unknown;
};
};
Runtime 提供:
context:用户传入的上下文数据(通过contextSchema定义)configurable:可配置参数(如thread_id)signal:取消信号(AbortSignal)writer:流式写入器interrupt:人机协作中断函数
27.6 子 Agent 的上下文传递
在 supervisor 模式中,子 Agent 可以通过 getCurrentTaskInput 访问父 Agent 的状态:
import { getCurrentTaskInput } from "@langchain/langgraph";
const scheduleEvent = tool(
async ({ request }, config) => {
// 获取父 Agent 线程中的消息
const currentMessages = getCurrentTaskInput<BuiltInState>(config).messages;
const originalUserMessage = currentMessages.find(HumanMessage.isInstance);
const prompt = `
用户原始请求:${originalUserMessage?.content}
子任务:${request}
`;
const result = await calendarAgent.invoke({
messages: [{ role: "user", content: prompt }],
});
return result.messages.at(-1)?.text;
},
{ name: "schedule_event", description: "...", schema: z.object({ ... }) }
);
27.7 多 Agent + Human-in-the-Loop
结合 humanInTheLoopMiddleware,可以在子 Agent 执行敏感操作前暂停等待人工审批:
import { humanInTheLoopMiddleware, createAgent } from "langchain";
import { MemorySaver, Command } from "@langchain/langgraph";
const calendarAgent = createAgent({
model: llm,
tools: [createCalendarEvent],
middleware: [
humanInTheLoopMiddleware({
interruptOn: { create_calendar_event: true },
descriptionPrefix: "日历事件待审批",
}),
],
});
// supervisor 使用 checkpointer 保存中断状态
const supervisor = createAgent({
model: llm,
tools: [scheduleEventTool],
checkpointer: new MemorySaver(),
});
const config = { configurable: { thread_id: "session-1" } };
// 第一次调用 — 会在工具执行前中断
const stream = await supervisor.stream(
{ messages: [{ role: "user", content: "安排明天2点的会议" }] },
config
);
// 收集中断信息
for await (const step of stream) { /* 处理中断 */ }
// 审批后恢复执行
const resumeStream = await supervisor.stream(
new Command({ resume: { [interruptId]: { decisions: [{ type: "approve" }] } } }),
config
);
27.8 设计多 Agent 系统的原则
- 最小权限:每个子 Agent 只拥有其职责所需的工具
- 清晰的接口:子 Agent 通过工具描述定义能力边界
- 独立的上下文:子 Agent 有自己的对话历史,避免上下文污染
- 统一的错误处理:supervisor 层面统一处理子 Agent 的失败
27.9 实战练习:构建"研究助手"系统
import { z } from "zod";
import { createAgent, tool } from "langchain";
// 研究员 Agent
const researcherAgent = createAgent({
model: "openai:gpt-4o",
tools: [searchTool, readUrlTool],
systemPrompt: "你是一个研究员,负责搜索和收集信息。",
name: "researcher",
});
// 撰写员 Agent
const writerAgent = createAgent({
model: "openai:gpt-4o",
tools: [],
systemPrompt: "你是一个技术写手,根据收集的信息撰写报告。",
name: "writer",
});
// 审核员 Agent
const reviewerAgent = createAgent({
model: "openai:gpt-4o",
tools: [],
systemPrompt: "你是一个审核员,检查报告的准确性和完整性。",
name: "reviewer",
});
// 封装为工具
const research = tool(
async ({ topic }) => {
const result = await researcherAgent.invoke({
messages: [{ role: "user", content: `研究:${topic}` }],
});
return result.messages.at(-1)?.text ?? "";
},
{
name: "research",
description: "搜索和收集指定主题的信息",
schema: z.object({ topic: z.string() }),
}
);
const writeReport = tool(
async ({ content }) => {
const result = await writerAgent.invoke({
messages: [{ role: "user", content: `根据以下信息撰写报告:\n${content}` }],
});
return result.messages.at(-1)?.text ?? "";
},
{
name: "write_report",
description: "根据研究信息撰写报告",
schema: z.object({ content: z.string() }),
}
);
const reviewReport = tool(
async ({ report }) => {
const result = await reviewerAgent.invoke({
messages: [{ role: "user", content: `审核以下报告:\n${report}` }],
});
return result.messages.at(-1)?.text ?? "";
},
{
name: "review_report",
description: "审核报告的准确性和完整性",
schema: z.object({ report: z.string() }),
}
);
// Supervisor Agent
const supervisor = createAgent({
model: "openai:gpt-4o",
tools: [research, writeReport, reviewReport],
systemPrompt: `你是研究团队的主管。工作流程:
1. 使用 research 工具收集信息
2. 使用 write_report 工具撰写报告
3. 使用 review_report 工具审核报告`,
});
const result = await supervisor.invoke({
messages: [{ role: "user", content: "写一篇关于 LangChain.js Agent 系统的技术报告" }],
});
27.10 源码精读路线
| 优先级 | 文件 | 关注点 |
|---|---|---|
| P0 | examples/src/multi-agent/subagents-personal-assistant.ts | 完整的 supervisor 多 Agent 示例 |
| P0 | examples/src/createAgent/supervisor.ts | supervisor + 子 Agent 封装为工具 |
| P1 | agents/withAgentName.ts | Agent 名称的编码/解码 |
| P1 | agents/utils.ts:97-234 | _addInlineAgentName / _removeInlineAgentName |
| P2 | agents/runtime.ts | Runtime 类型定义与上下文传递 |
| P2 | agents/types.ts:522-881 | CreateAgentParams — 完整的 Agent 创建参数 |
本课收获总结
| 级别 | 你应该掌握的 |
|---|---|
| 🟢 基础 | 理解多 Agent 的典型场景:分工、审核、辩论 |
| 🔵 中阶 | 学会用 supervisor 模式编排多个 Agent:子 Agent 封装为工具 |
| 🟡 高阶 | 掌握 Agent 间的消息传递(withAgentName)与上下文共享(getCurrentTaskInput) |
| 🟠 资深 | 分析多 Agent 的错误传播与恢复策略;结合 human-in-the-loop |
| 🔴 架构 | 设计可扩展的多 Agent 架构:最小权限、清晰接口、独立上下文 |
下一课预告
第 28 课深入生产级 Agent 的错误处理、限流与安全 —— 理解 Agent 错误类型、最大迭代控制、超时机制和工具调用安全。