import {
StateGraph,
START,
END,
StateSchema,
MessagesValue,
Command,
ConditionalEdgeRouter,
GraphNode,
} from "@langchain/langgraph";
import { createAgent, AIMessage, ToolMessage } from "langchain";
import { tool, ToolRuntime } from "@langchain/core/tools";
import { z } from "zod/v4";
// 1. 定义带有 active_agent 跟踪器的状态
const MultiAgentState = new StateSchema({
messages: MessagesValue,
activeAgent: z.string().optional(),
});
// 2. 创建交接工具
const transferToSales = tool(
async (_, runtime: ToolRuntime<typeof MultiAgentState.State>) => {
const lastAiMessage = [...runtime.state.messages]
.reverse()
.find(AIMessage.isInstance);
const transferMessage = new ToolMessage({
content: "Transferred to sales agent from support agent",
tool_call_id: runtime.toolCallId,
});
return new Command({
goto: "sales_agent",
update: {
activeAgent: "sales_agent",
messages: [lastAiMessage, transferMessage].filter(Boolean),
},
graph: Command.PARENT,
});
},
{
name: "transfer_to_sales",
description: "Transfer to the sales agent.",
schema: z.object({}),
}
);
const transferToSupport = tool(
async (_, runtime: ToolRuntime<typeof MultiAgentState.State>) => {
const lastAiMessage = [...runtime.state.messages]
.reverse()
.find(AIMessage.isInstance);
const transferMessage = new ToolMessage({
content: "Transferred to support agent from sales agent",
tool_call_id: runtime.toolCallId,
});
return new Command({
goto: "support_agent",
update: {
activeAgent: "support_agent",
messages: [lastAiMessage, transferMessage].filter(Boolean),
},
graph: Command.PARENT,
});
},
{
name: "transfer_to_support",
description: "Transfer to the support agent.",
schema: z.object({}),
}
);
// 3. 创建带有交接工具的代理
const salesAgent = createAgent({
model: "anthropic:claude-sonnet-4-20250514",
tools: [transferToSupport],
systemPrompt:
"You are a sales agent. Help with sales inquiries. If asked about technical issues or support, transfer to the support agent.",
});
const supportAgent = createAgent({
model: "anthropic:claude-sonnet-4-20250514",
tools: [transferToSales],
systemPrompt:
"You are a support agent. Help with technical issues. If asked about pricing or purchasing, transfer to the sales agent.",
});
// 4. 创建调用代理的代理节点
const callSalesAgent: GraphNode<typeof MultiAgentState.State> = async (state) => {
const response = await salesAgent.invoke(state);
return response;
};
const callSupportAgent: GraphNode<typeof MultiAgentState.State> = async (state) => {
const response = await supportAgent.invoke(state);
return response;
};
// 5. 创建路由器,检查是否应结束或继续
const routeAfterAgent: ConditionalEdgeRouter<
typeof MultiAgentState.State,
"sales_agent" | "support_agent"
> = (state) => {
const messages = state.messages ?? [];
// 检查最后一条消息 - 如果是不带工具调用的 AIMessage,则完成
if (messages.length > 0) {
const lastMsg = messages[messages.length - 1];
if (lastMsg instanceof AIMessage && !lastMsg.tool_calls?.length) {
return END;
}
}
// 否则路由到活动代理
const active = state.activeAgent ?? "sales_agent";
return active as "sales_agent" | "support_agent";
};
const routeInitial: ConditionalEdgeRouter<
typeof MultiAgentState.State,
"sales_agent" | "support_agent"
> = (state) => {
// 根据状态路由到活动代理,默认为销售代理
return (state.activeAgent ?? "sales_agent") as
| "sales_agent"
| "support_agent";
};
// 6. 构建图
const builder = new StateGraph(MultiAgentState)
.addNode("sales_agent", callSalesAgent)
.addNode("support_agent", callSupportAgent);
// 根据初始 activeAgent 开始条件路由
.addConditionalEdges(START, routeInitial, [
"sales_agent",
"support_agent",
])
// 每个代理之后,检查是否应结束或路由到另一个代理
.addConditionalEdges("sales_agent", routeAfterAgent, [
"sales_agent",
"support_agent",
END,
]);
builder.addConditionalEdges("support_agent", routeAfterAgent, [
"sales_agent",
"support_agent",
END,
]);
const graph = builder.compile();
const result = await graph.invoke({
messages: [
{
role: "user",
content: "Hi, I'm having trouble with my account login. Can you help?",
},
],
});
for (const msg of result.messages) {
console.log(msg.content);
}