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: "已从支持代理转接给销售代理",
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: "转接给销售代理。",
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: "已从销售代理转接给支持代理",
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: "转接给支持代理。",
schema: z.object({}),
}
);
// 3. 创建带交接工具的代理
const salesAgent = createAgent({
model: "anthropic:claude-sonnet-4-20250514",
tools: [transferToSupport],
systemPrompt:
"你是一个销售代理。帮助处理销售咨询。如果被问及技术问题或支持,请转接给支持代理。",
});
const supportAgent = createAgent({
model: "anthropic:claude-sonnet-4-20250514",
tools: [transferToSales],
systemPrompt:
"你是一个支持代理。帮助处理技术问题。如果被问及定价或购买,请转接给销售代理。",
});
// 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: "嗨,我的帐户登录有问题。你能帮忙吗?",
},
],
});
for (const msg of result.messages) {
console.log(msg.content);
}